事务
什么是事务?
在实际的开发过程中,一个业务操作如:转账,往往是要多次访问数据库才能完成的。转
账是一个用户扣钱,另一个用户加钱。如果其中有一条
SQL
语句出现异常,这条
SQL
就可能执行失败;
事务执行是一个整体,所有的
SQL
语句都必须执行成功。如果其中有
1
条
SQL
语句出现异常,则所有的SQL 语句都要回滚,整个业务执行失败;
手动提交事务
MYSQL
中可以有两种方式进行事务的操作:
- ?手动提交事务
- ?自动提交事务
手动提交事务的sql语句
开启事务? ??
start transaction;
手动提交事务使用过程:?
执行成功的情况: 开启事务 --->
执行多条
SQL
语句 --->
?
成功提交事务
执行失败的情况: 开启事务 --->
?
执行多条
SQL
语句 --->
?
事务的回滚
自动提交事务

?MySQL 默认每一条 DML(增删改)语句都是一个单独的事务,每条语句都会自动开启一个事务,语句执行完毕 自动提交事务,MySQL 默认开始自动提交事务;
取消自动提交
查看
MySQL
是否开启自动提交事务
@@
表示全局变量,
1
表示开启,
0
表示关闭
取消自动提交事务
执行更新语句,使用
SQLYog
查看数据库,发现数据并没有改变
在控制台执行
commit
提交任务
事务原理
事务开启之后
,
所有的操作都会临时保存到事务日志中
,
事务日志只有在得到
commit
命令才会同步到数据表中,其他任何情况都会清空事务日志(rollback
,断开连接
)
事务的步骤:?
- 客户端连接数据库服务器,创建连接时创建此用户临时日志文件
- 开启事务以后,所有的操作都会先写入到临时日志文件中
- 所有的查询操作从表中查询,但会经过日志文件加工后才返回
- 如果事务提交则将日志文件中的数据写到表中,否则清空日志文件。
回滚点 ?
在某些成功的操作完成之后,后续的操作有可能成功有可能失败,但是不管成功还是失败,前面操作都已经成 功,可以在当前成功的位置设置一个回滚点。可以供后续失败操作返回到该位置,而不是返回所有操作,这个点称之 为回滚点。
回滚点的操作语句?
设置回滚点 ????????savepoint 名字
回到回滚点 ????????rollback to 名字
?注意事项:设置回滚点可以让我们在失败的时候回到回滚点,而不是回到事务开启的时候。
事务的四大特性 ACID ?
原子性(
Atomicity
)
每个事务都是一个整体,不可再拆分,事务中所有的
SQL
语句要么都执行成功,
要么都失败。
一致性(
Consistency
)
事务在执行前数据库的状态与执行后数据库的状态保持一致。如:转账前
2
个人的
总金额是
2000
,转账后
2
个人总金额也是
2000
隔离性(
Isolation
)
事务与事务之间不应该相互影响,执行时保持隔离的状态。
持久性(
Durability
)
一旦事务执行成功,对数据库的修改是持久的。就算关机,也是保存下来的。
事务的隔离级别
事务在操作时的理想状态: 所有的事务之间保持隔离,互不影响。因为并发操作,多个用户同时访问同一个 数据。可能引发并发访问的问题:
脏读 ??????????????
一个事务读取到了另一个事务中尚未提交的数据
不可重复读 ??????
?????????
一个事务中两次读取的数据
内容
不一致,要求的是一个事务中多次读取时数据是一致的,这
是事务
update
时引发的问题
幻读
一个事务中两次读取的数据的
数量
不一致,要求在一个事务多次读取的数据的数量是一致
的,这是
insert
或
delete
时引发的问题
MySQL 数据库有四种隔离级别
上面的级别最低,下面的级别最高。“是”表示会出现这种问题,“否”表示不会出现这种问题。

注意:?隔离级别越高,性能越差,安全性越高
MySQL 事务隔离级别相关的命令
查询全局事务隔离级别
select @@tx_isolation;
设置事务隔离级别,需要退出
MySQL
再重新登录才能看到隔离级别的变化
set global transaction isolation level? 级别字符串
JDBC:Java database? connectivity(Java数据库连接)
?JDBC的本质
???Java连接数据库 ? 由数据库厂商提供的数据库jar包,实现了sun提供的接口的实现类 ? Java.sql.Driver 驱动接口------------>com.mysql.jdbc.Driver ? java.sql.Connection 数据库连接接口--------->com.mysql.jdbc.ConnectionImpl ? 只需要导入数据库厂商提供的jar包 ? jar包的名字: mysql-connection-Java.版本号.jar
JDBC的操作步骤
?1 导包 ?2 注册驱动?? ------>Class.forName("com.mysql.jdbc.Driver"); ?3 获取数据库连接对象--------->(三个参数:协议,(jdbc:mysql://localhost:3306/库名)用户名,密码) ?4 写sql语句------------>(DDL 创建表/ insert into/ update /delete ?from) ?5 连接对象获取执行对象 ?------->(Statement stmt=conn.createStatement();) ?6 执行sql语句,---------->? resultSet=stmt.excuteQuery(sql); ?7 释放资源------>(conn.close(); ? stmt.close(); )
代码提现:
package com.qf.jdbc_01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
*
* jdbc原生操作步骤:7大步骤
* 1)导入驱动mysql的jar包
* 2)注册驱动
* 3)创建数据库的连接对象Connection
* 4)准备好sql
* 5)通过连接对象获取执行对象Statment
* 6)执行sql语句 (插入操作)
* 7)释放资源 (系统资源需要被释放的)
*/
public class JDBCDemo {
public static void main(String[] args) throws Exception {
//1)导入驱动mysql的jar包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver") ;
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//3)创建数据库的连接对象Connection
//jdk提供的DriverManager:驱动管理类(管理jdbc的驱动的)
//public static Connection getConnection(String url,String user,String password)throws SQLException
//参数url: 统一资源定义符号 组成 协议:端口号:库名 --->jdbc:mysql://localhost:3306
//参数user: 登录msyql的用户名 root用户
//参数password:登录mysql的密码
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ee_2208_02",
"root",
"123456"
);
//4)准备好sql
String sql = "insert into account(name,balance) values('赵又廷2',2000) " ;
//5)通过连接对象获取执行对象Statment
//Statement createStatement()throws SQLException创建一个Statement对象,用于将SQL语句发送到数据库
Statement stmt = conn.createStatement();
//6)执行sql语句 (插入操作)
//Statement执行器里面---->
//int executeUpdate(String sql)throws SQLException 通用的执行通用添加,删除,修改
int count = stmt.executeUpdate(sql);
System.out.println("影响了"+count+"行") ;
//7)释放资源 (系统资源需要被释放的)
stmt.close();
conn.close();
}
}
package com.qf.jdbc_02;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
* JDBC来操作DDL语句:创建表
* 使用JDBC来创建一个学生表,编号id,name姓名,age年龄,gender性别,address住址
*
*/
public class JdbcDemo2 {
public static void main(String[] args) throws Exception{
//7大步骤
//1)导包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver") ;
//3)获取数据库连接对象
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ee_2208_02",
"root",
"123456");
//4)准备sql
String sql = "create table student(" +
" id int primary key auto_increment," +
" name varchar(10)," +
" age int," +
" gender varchar(3)," +
" address varchar(20)" +
");" ;
//5)通过连接对象获取执行器
Statement stmt = conn.createStatement();
//6)执行sql语句 ----通用的更新操作:添加删除/修改, 或者ddl语句 "建表" ---int executeUpdate(String sql)
int count = stmt.executeUpdate(sql);
System.out.println(count);
//7)释放资源
stmt.close();
conn.close();
}
}
package com.qf.jdbc_02;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Jdbc操作DML语句
* 插入,insert
* 修改,update
* 删除,delete
*
* 加入异常处理----try...catch..finally...捕获异常
*/
public class JDBCDemo3 {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
try {
//1)导包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver") ;
//3)获取数据库连接对象
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ee_2208_02",
"root",
"123456"
);
//4)sql语句
//插入insert
/* String sql = "insert into student(name,age,gender,address) values('李帅',25,'男','西安市')," +
" ('高圆圆',28,'女','渭南市')," +
" ('赵又廷',30,'男','宝鸡市')," +
" ('王瑄',23,'男','南窑国际')" ;*/
//修改操作update
//String sql = "update student set name = '马蓉',age = 25,address='渭南市富平县' where id = 2" ;
//删除
String sql = "delete from student where id = 2 ;" ;
//5)通过连接对象获取执行器
stmt = conn.createStatement();
//6)执行
int count = stmt.executeUpdate(sql);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close() ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
定义工具类
针对原生JDBC进行封装 将注册驱动以及获取连接对象,释放资源代码进行优化
package com.qf.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 针对原生JDBC进行封装 将注册驱动以及获取连接对象,释放资源代码进行优化
*/
public class JdbcUtils {
//提供几个属性
private static String url = null ;
private static String user = null ;
private static String password = null ;
private static String driverClass= null ;
//构造方法私有化
private JdbcUtils(){}
//提供静态代码块
//JdbcUtils 类一加载,static{}就加载了,
static{
try {
//1)读取src下面的配置文件jdbc.properties
InputStream inputStream = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//2)将配置文件的内容加载到属性集合列表Properties
//创建一个空的属性集合列表
Properties prop = new Properties() ;
prop.load(inputStream);
//System.out.println(prop);
//3)通过配置文件的key获取value
//4)就给上面的成员变量赋值
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url") ;
user = prop.getProperty("user") ;
password = prop.getProperty("password") ;
//注册驱动
Class.forName(driverClass) ;
} catch (Exception e) {
e.printStackTrace();
}
}
//对外提供静态方法
//定义一个方法:获取连接对象
public static Connection getConnection(){
try {
//需要用到驱动管理类,获取连接对象
Connection conn = DriverManager.getConnection(url, user, password);
return conn ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
/**
* 释放资源 针对DQL语句,select语句
* @param rs 释放结果集对象
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(ResultSet rs,Statement stmt,Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 释放资源,针对DML语句:update,delete,insert操作的
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);//复用上面的close,第一个参数为null
}
public static void main(String[] args) {
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
}
}
测试,运用工具类
import java.sql.Statement;
/**
* 定义工具类,测试工具类
*/
public class JDBCUtilsTest {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null ;
try {
//直接获取数据库连接对象
conn = JdbcUtils.getConnection();
//sql
String sql = "insert into student(name,age,gender,address) values('文章',35,'男','咸阳市')" ;
//获取执行对象
stmt = conn.createStatement();
//执行
int count = stmt.executeUpdate(sql);
System.out.println(count);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//释放资源
JdbcUtils.close(stmt,conn);
}
}
}
?使用JDBC来操作DQL语句(数据库查询语句)
package com.qf.jdbc_operator_dql_02;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 使用JDBC来操作DQL语句(数据库查询语句)
* 查询ee_2208_02库中的学生表的所有信息,展示数据
*/
public class JdbcOperatorDQL {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
//注册驱动
//获取连接
conn = JdbcUtils.getConnection();
//sql
String sql = "select * from student" ;
//通过连接获取执行对象
stmt = conn.createStatement();
//执行查询---ResuletSet executeQuery(String sql)
rs = stmt.executeQuery(sql);
//第一次查
//如果有数据---,才移动一行 boolean next() :有数据,移动
System.out.println("学生信息如下:");
System.out.println("学生编号\t学生姓名\t学生年龄\t学生性别\t住址");
//循环
while(rs.next()){
//方式2:通过列的名称获取 xxx getXXX(String columnLabel)
int id = rs.getInt("id") ;
String name = rs.getString("name") ;
int age = rs.getInt("age") ;
String gender = rs.getString("gender") ;
String address = rs.getString("address") ;
System.out.println(id+"\t\t"+name+"\t\t"+age+"\t\t"+gender+"\t\t"+address);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//释放资源
JdbcUtils.close(rs,stmt,conn);
}
}
}
JDBC的测试,自定义一个学生类,属性和表中的字段一一对应
package com.qf.jdbc_test_03;
/**
* 创建学生类
*/
public class Student {
//属性和student表的字段一一对应 ,属性私有化
private int id ;
private String name ;
private int age ;
private String gender ;
private String address ;
//无参构造方法
public Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
'}';
}
}
JDBC的应用?
package com.qf.jdbc_test_03;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* JDBC的应用
* 需求:查询student表的所有数据,将每一条记录封装到Student对象中,将这些学生对象添加到
* List<Student>,将List集合遍历,获取每一个学生对象的信息!
*
* 分析:
* 1)学生类 Student---->id,name,age,gender,address属性(属性都是小写)
* 2)在jdbcTest定义一个方法---->返回List<Student>
* 2.1)完成jdbc操作,查询student表
* 查询一条,封装一个Student对象
* 2.2)创建一个空的List<Student>,将封装学生对象添加到list集合对象汇总
* 3)main方法去调用自定义的方法
*/
public class JdbcTest {
public static void main(String[] args) throws Exception {
List<Student> students = getStudents();
if(students!=null){
for(Student s:students){
System.out.println(s);
}
}
}
/**
* 获取学生信息
* @return 学生列表
*/
public static List<Student> getStudents() throws Exception {
//jdbc操作
//注册驱动,获取数据库连接
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "select * from student";
//通过数据库的连接对象获取执行对象
Statement stmt = conn.createStatement();
//执行sql
ResultSet rs = stmt.executeQuery(sql);
//创建一个空的集合
List<Student> list = new ArrayList<>();
//声明一个学生类型的变量
Student s = null;
//遍历结果集
while (rs.next()) { //不断在判断:有数据---一动一行---封装一个学生对象
//封装学生对象
s = new Student();
//通用方式1方式2获取 ,列名称或者列的索引值
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String gender = rs.getString("gender");
String address = rs.getString("address");
s.setId(id);
s.setName(name);
s.setAge(age);
s.setGender(gender);
s.setAddress(address);
//将学生对象s添加list集合中
list.add(s);
}
//释放资源
JdbcUtils.close(rs,stmt,conn);
return list;
}
}
实例:
使用Statement执行对象完成模拟用户登录操作,比如有一个user表(id字段,name,password字段), 有两个用户信息?? ?,查询user表的指定用户,如果找到用户,则提示"登录成功",否则"登录失败!"
package com.qf.jdbc_test_03;
import com.qf.utils.JdbcUtils;
import java.sql.*;
import java.util.Scanner;
public class JdbcTest2 {
public static void main(String[] args) throws SQLException {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请您输入用户名:") ;
String username = sc.nextLine() ;
System.out.println("请您输入密码:") ;
String password = sc.nextLine() ;
//调用isLogin方法
// boolean flag = isLogin(username, password);
boolean flag = isLogin2(username, password);
if(flag){
//true
System.out.println("登录成功");
}else{
System.out.println("登录失败!");
}
}
//预编译对象:PreparedStatement
//两个通用操作:更新 int executeUpdate() /ResultSet executeQuery()
public static boolean isLogin2(String username,String password) throws SQLException {
//注册驱动,获取数据库连接
Connection conn = JdbcUtils.getConnection();
//sql
//预编译sql语句:里面的参数值都是英文的 ?--一个?代表一个值 就是占位符
String sql = "select * from user where username = ? and password = ?" ;
System.out.println(sql) ;
//获取执行对象---->预编译对象PreparedStatement
//PreparedStatement prepareStatement(String sql):将上面的参数化sql发送给数据库
//将sql语句存储到了预编译对象中
PreparedStatement stmt = conn.prepareStatement(sql);
//给占位符赋值
//通用setXXX(占位符号索引值,实际值) ; // 占位符号索引值是从1开始,第一个?就是1,第二个?就是 2
//举例:setString(1,username)
stmt.setString(1,username);
stmt.setString(2,password);
//执行查询
///ResultSet executeQuery()
ResultSet rs = stmt.executeQuery();
boolean flag = rs.next();
//释放资源
JdbcUtils.close(rs,stmt,conn);
return flag ; //查到了返回true
}
}
Statement弊端
select * from user where username = 'sdfs' and password = ''OR' ?1= 1' 存储在字符串拼接sql语句 会造成SQL注入,不安全! 执行效率低,每一次写好一个sql,才能发送给数据库,写一个,发送一个!频繁和数据库的交互(次数比较频繁)
?PreparedStatement的特点
不会造成sql注入,不存在sql拼接 执行效率相对Statement,将sql编译一次发送数据库,sql语句存储PreparedStatement预编译对象 里面给赋不同的值即可! ?? ?
statement和preparedstatement区别??
共同点:都能将sql发送给数据库,,都称为执行对象,,后者继承前者
区别:
?1.是否能够造成sql注入 Statement, ------由于操作sql语句是静态的,存在字符串拼接,存在安全漏洞 preparedStatement(预编译对象)可以防止sql注入 -----发送的sql数据是一种参数化的sql,不存在拼接, ? ? ? ? 举例: ? ? ? ? ? ? ? ? insert into 表名 values(?,?,?,?,?); ?//? ?占位符号
2.sql执行效率 ? ? ? ? ? ?Statement执行sql语句,效率比较低,执行静态sql ? ? ? ? ? ?举例, ? ? ? ? ? ?insert into 表名 ?values (1,''张三,30,'男'','西安市') ? ? ? ? ? ?写sql语句,写一句执行一句,执行效率低 ? ? ? ? ? ?PreparedStatement执行效率高,执行参数化的sql
单元测试?
单元测试 导入jar包 junit-4.13.1 ?依赖包hamcrest-core.jar 编写方法---没有返回值,没有参数 使用@Test标记这个方法是单元测试方法--能够单独驱动的(包含启动器runner,直接运行的) @Before:标记的方法是在@Test标记的单元测试方法先执行---->一般初始化的代码---读取配置文件等操作,对象创建 @After:标记的方法是在@Test标记的单元测试方法之后执行----> 一般释放资源代码 测试功能 org.junit.Assert类--->断言 ?---针对具体数据结果值直接用它 开发中,还是直接测试自己的功能,是否能够从数据库中将数据获取到
代码提现:
package com.qf.junit_04;
/**
* 定义一个计算器类
*/
public class Calculator {
//加
public int add(int a,int b){
return a + b ;
}
//减
public int sub(int a, int b){
return a -b ;
}
//乘
public int mul(int a,int b){
return a * b ;
}
//除
public int div(int a,int b){
return a/b ;
}
}
package com.qf.junit_04;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 针对计算器类的测试类
*
*
* 单元测试
* 1)导入jar包 junit-4.13.1 依赖包hamcrest-core.jar
* 2)编写方法---没有返回值,没有参数
* 3)使用@Test标记这个方法是单元测试方法--能够单独驱动的(包含启动器runner,直接运行的)
*
* @Before:标记的方法是在@Test标记的单元测试方法先执行---->一般初始化的代码---读取配置文件等操作,对象创建
* @After:标记的方法是在@Test标记的单元测试方法之后执行----> 一般释放资源代码
* 4)测试功能
*
* org.junit.Assert类--->断言 ---针对具体数据结果值直接用它
*
* 开发中,还是直接测试自己的功能,是否能够从数据库中将数据获取到
*
*/
public class CalculatorTest {
Calculator c = null ;
@Before
public void init(){
System.out.println("init方法执行了...");
//创建计算器类
c = new Calculator() ;
}
@After //@After:标记的方法是在@Test标记的单元测试方法之
// 后执行
public void close(){
System.out.println("close方法执行了....");
}
/**
* 测试计算器类的求和功能
*/
@Test
public void testAdd(){
System.out.println("testAdd方法执行了");
//1)编写Java代码
//创建计算器类
//Calculator c = new Calculator() ;
int reuslt = c.add(10, 20); //求和
//断言----result结果值和预期结果值是否相等,如果相等,断言成功;否则,断言失败
//public static void assertEquals(Object expected, Object actual) :参数1:预期值,参数2:实际值
Assert.assertEquals(30,reuslt);
}
}
案例,对于员工表进行操作
定义员工实体类
/**
* 员工类
*/
public class Employee {
//和员工表的字段一致
private int id ;//编号
private String name ;//姓名
private int age ;//年龄
private String gender ; //性别
private String address ; //地址
private Date birthday ;//出生日期
public Employee() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
", birthday=" + birthday +
'}';
}
}
工具类用到的属性文件?
driverClass=com.mysql.jdbc.Driver? ? ?? url=jdbc:mysql://localhost:3306/库名 user=root????????????????? //用户名 password=******?????????//密码
?简易工具类Utils
/**
* 针对原生JDBC进行封装 将注册驱动以及获取连接对象,释放资源代码进行优化
*/
public class JdbcUtils {
//提供几个属性
private static String url = null ;
private static String user = null ;
private static String password = null ;
private static String driverClass= null ;
//构造方法私有化
private JdbcUtils(){}
//提供静态代码块
//JdbcUtils 类一加载,static{}就加载了,
static{
try {
//1)读取src下面的配置文件jdbc.properties
InputStream inputStream = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//2)将配置文件的内容加载到属性集合列表Properties
//创建一个空的属性集合列表
Properties prop = new Properties() ;
prop.load(inputStream);
//System.out.println(prop);
//3)通过配置文件的key获取value
//4)就给上面的成员变量赋值
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url") ;
user = prop.getProperty("user") ;
password = prop.getProperty("password") ;
//注册驱动
Class.forName(driverClass) ;
} catch (Exception e) {
e.printStackTrace();
}
}
//对外提供静态方法
//定义一个方法:获取连接对象
public static Connection getConnection(){
try {
//需要用到驱动管理类,获取连接对象
Connection conn = DriverManager.getConnection(url, user, password);
return conn ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
/**
* 释放资源 针对DQL语句,select语句
* @param rs 释放结果集对象
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(ResultSet rs,Statement stmt,Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 释放资源,针对DML语句:update,delete,insert操作的
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);//复用上面的close,第一个参数为null
}
public static void main(String[] args) {
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
}
}
员工数据访问接口
package com.qf.dao;
import com.qf.pojo.Employee;
import java.sql.SQLException;
import java.util.List;
/**
* 针对员工的数据访问接口
*/
public interface EmployeDao {
/**
* 查询员工表的所有数据,将员工表的数据封装List集合中
* @return 返回员工列表
*/
List<Employee> findAll() throws SQLException;
/**
* 通过员工编号查询员工,将员工这条信息封装员工类中
* @param id 已知员工编号
* @return 返回员工实体
*/
Employee findEmployeeById(int id) throws SQLException;
/**
* 添加员工对象
* @param employee 员工对象
*/
void addEmplyee(Employee employee) throws SQLException;
/**
* 修改员工数据
* @param employee 要修改员工实体
*/
void updateEmployee(Employee employee) throws SQLException;
/**
* 通过id删除员工
* @param id
*/
void deleteEmployee(int id) throws SQLException;
}
员工数据访问接口实现
package com.qf.dao.impl;
import com.qf.dao.EmployeDao;
import com.qf.pojo.Employee;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* 针对员工的数据访问接口实现
*/
public class EmployeeDaoImpl implements EmployeDao {
/**
* 查询员工表的所有数据,将员工表的数据封装List集合中
* @return 返回员工列表
*/
@Override
public List<Employee> findAll() throws SQLException {
//创建List集合
List<Employee> list = new ArrayList<>() ;
//注册驱动.获取数据库连接对象
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "select * from employee" ;
//创建执行对象
Statement stmt = conn.createStatement();
Employee emp = null ;
//执行查询
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
//封装员工实体
emp = new Employee();
emp.setId(rs.getInt("id"));
emp.setName(rs.getString("name"));
emp.setAge(rs.getInt("age"));
emp.setGender(rs.getString("gender"));
emp.setAddress(rs.getString("address"));
emp.setBirthday(rs.getDate("birthday"));
//将emp对象添加list集合中
list.add(emp) ;
}
//释放资源
JdbcUtils.close(rs,stmt,conn);
return list;
}
/**
* 通过员工编号查询员工,将员工这条信息封装员工类中
* @param id 已知员工编号
* @return 返回员工实体
*/
@Override
public Employee findEmployeeById(int id) throws SQLException {
//注册驱动,获取数据库连接对象
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "select * from employee where id = "+id+"" ;
//获取执行对象
Statement stmt = conn.createStatement();
//执行查询
ResultSet rs = stmt.executeQuery(sql);
Employee employee = null ;
while(rs.next()){
//封装员工实体
employee = new Employee() ;
employee.setId(rs.getInt("id"));
employee.setName(rs.getString("name"));
employee.setAge(rs.getInt("age"));
employee.setGender(rs.getString("gender"));
employee.setAddress(rs.getString("address"));
employee.setBirthday(rs.getDate("birthday"));
}
//释放资源
JdbcUtils.close(rs,stmt,conn);
return employee;
}
/**
* 添加员工对象
* @param employee 员工对象
*/
@Override
public void addEmplyee(Employee employee) throws SQLException {
//注册驱动,获取数据库连接对象
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "insert into employee(name,age,gender,address,birthday) " +
"values('" + employee.getName() + "','" +employee.getAge()+ "','" + employee.getGender() + "'," +
"'" + employee.getAddress() + "','" + employee.getBirthday() + "')";
//测试sql
System.out.println(sql);
//获取执行对象
Statement stmt = conn.createStatement();
//执行更新
int count = stmt.executeUpdate(sql);
System.out.println(count);
//释放资源
JdbcUtils.close(stmt,conn);
}
/**
*
* @param employee 要修改员工实体
*/
@Override
public void updateEmployee(Employee employee) throws SQLException {
//注册驱动,获取连接对象
Connection connection = JdbcUtils.getConnection();
//sql
String sql = "update employee set name ='"+employee.getName()+"' ," +
"age ='"+employee.getAge()+"',gender='"+employee.getGender()+"',address='"+employee.getAddress()+"' where id ='"+employee.getId()+"' ";
System.out.println(sql) ;
//获取执行对象
Statement stmt = connection.createStatement();
//执行更新
int count = stmt.executeUpdate(sql);
System.out.println(count);
//释放资源
JdbcUtils.close(stmt,connection);
}
/**
* 删除员工
* @param id
*/
@Override
public void deleteEmployee(int id) throws SQLException {
//注册驱动,获取连接对象
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "delete from employee where id = '"+id+"' " ;
System.out.println(sql);
//获取执行对象
Statement stmt = conn.createStatement() ;
//执行
int count = stmt.executeUpdate(sql);
System.out.println(count);
//释放资源
JdbcUtils.close(stmt,conn);
}
}
针对员工类的测试
package com.qf.test;
import com.qf.dao.EmployeDao;
import com.qf.dao.impl.EmployeeDaoImpl;
import com.qf.pojo.Employee;
import org.junit.Before;
import org.junit.Test;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* 针对员工类的测试
*/
public class EmployeeTest {
//声明接口类型
private EmployeDao employeeDao ;
@Before
public void init(){
//创建接口对象----接口多态
employeeDao = new EmployeeDaoImpl() ;
}
/**
* 测试查询所有数据,将数据封装List集合汇总
*/
@Test
public void testFindAll() throws SQLException {
//调用功能
List<Employee> all = employeeDao.findAll();
if(all!=null){
for(Employee emp :all){
System.out.println(emp);
}
}
}
/**
* 测试通过id获取员工信息
*/
@Test
public void testFindEmpById() throws SQLException {
Employee emp = employeeDao.findEmployeeById(3);
if(emp==null){
System.out.println("没有这个员工");
}else{
System.out.println(emp);
}
}
/**
* 测试添加员工的功能
*/
@Test
public void testAddEmployee() throws Exception {
//创建一个员工
Employee emp = new Employee() ;
emp.setName("赵又廷") ;
emp.setAge(30);
emp.setGender("男");
emp.setAddress("西安市");
//将String--java.util.Date
String dateStr = "1984-02-10" ;//字符串日期文本
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
Date date = sdf.parse(dateStr);
//java.sql.Date有参构造方法
//public java.sql.Date(long time)
java.sql.Date myDate = new java.sql.Date(date.getTime()) ;
System.out.println(myDate);
emp.setBirthday(myDate);
employeeDao.addEmplyee(emp);
}
/**
* 测试修改员工
*/
@Test
public void testUpdateEmp() throws SQLException {
Employee emp = new Employee() ;
emp.setId(7) ;
emp.setName("张佳宁") ;
emp.setAge(32) ;
emp.setGender("女");
emp.setAddress("西安市鄠邑区") ;
employeeDao.updateEmployee(emp);
}
/**
* 测试删除员工
*/
@Test
public void testDeleteEmp() throws SQLException {
employeeDao.deleteEmployee(3);
}
}
JDBC控制事务
void setAutoCommit(boolean flag)? true? 表示自动提交,false 表示禁用自动提交(手动提交)
void rollback() ; 事务回滚
void commit() ;? 提交事务
package com.qf.jdbc_control_tansaction_01;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Jdbc :Java连接数据库的方式控制事务
* java.sql.Connection接口
*
*void setAutoCommit(boolean autoCommit) throws SQLException
* 是否开启手动提交,参数true,就是自动提交,false,禁用自动提交,手动提交----->相当于mysql的指令 start transaction;
*void rollback() throws SQLException撤销在当前事务中所做的所有更改----->事务回滚方法---->相当于msyql指令 rollback;
* void commit()throws SQLException使上次提交/回滚之后所做的所有更改都将永久性 (提交事务)---->mysql的指令 commit;
*
* Jdbc控制事务:当某个业务执行过程中需要同时指定多个sql(添加/删除/修改)需要将这个多个sql看成一个整体,他们要么
* 同时执行成功,要么同时执行失败!
*
* 需求:
* account表:针对多个账户更新操作,"转账的操作"
*
* 高圆圆给赵又廷转账500
*/
public class JdbcDemo {
public static void main(String[] args) {
//现在使用JDBC控制事务
Connection conn = null ;
PreparedStatement ps = null ;
PreparedStatement ps2 = null ;
try {
//获取数据库连接对象
conn = JdbcUtils.getConnection();
//开启事务
//void setAutoCommit(boolean autoCommit) throws SQLException
conn.setAutoCommit(false) ;//禁用自动提交
//sql语句
String sql1 = "update account set balance = balance -? where id= ? " ;
String sql2 = "update account set balance = balance +? where id = ?" ;
//获取预编译对象
ps = conn.prepareStatement(sql1);
//赋值
ps.setInt(1,500);
ps.setInt(2,3);
//将sql2进行发送数据库,获取预编译对象
ps2 = conn.prepareStatement(sql2);
//赋值
ps2.setInt(1,500) ;
ps2.setInt(2,4);
//通过预编译对象执行sql
int count = ps.executeUpdate();
//操作过程中,给一段代码(有问题的代码)
//int i = 10 /0 ;//除数不能0 try中的某行代码一出问题,就执行catch语句,处理异常
int count2 = ps2.executeUpdate();
System.out.println(count+"---"+count2);
//更新完上面的所有操作,正常提交数据
conn.commit();
System.out.println("转账成功");
} catch (ArithmeticException throwables) {
try {
//回滚事务
//void rollback() throws SQLException撤销在当前事务中所做的所有更改---
conn.rollback();
//回滚完之后,手动提交
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace(); //交个jvm处理 将异常信息打印控制台
//System.out.println("出问题了...除数为0");
} catch (SQLException throwables) {
throwables.printStackTrace();
// System.out.println("SQL语句出问题了");
} finally {
//释放资源
JdbcUtils.close(ps,conn);
JdbcUtils.close(ps2,conn);
}
}
}
Druid连接池

?连接池(Druid)的好处是什么?

?加入连接池后,JDBCUtils工具类里面的步骤?
?1).提供ThreadLocal,代表当前线程 ?2).静态代码块---> ? ? ? ? ? ? ? ? a)读取连接池的配置文件--->初始化了配置参数 ? ? ? ? ? ? ? ? b)创建连接池--->DataSource---->druid.jar---提供com.alibaba.druid.pool.DruidDataSource 3).定义了一个方法,获取连接池--->DataSource 4).提供一个方法,获取连接对象,--加入了ThreadLocal--->判断当前线程中有连接对象,如果有,直接返回,没有的话,从连接池中获取连接对象,绑定到线程上
Druid封装工具类(德鲁伊)
package com.qf.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 加入连接池Druid工具类的封装
*
* 模拟真实场景---->高并发情况下,每一个线程的连接对象应该使用自己的连接对象---->
* 来自于连接池! jdk提供了这个类ThreadLocal<Connection>
*/
public class JdbcUtils_WithDruid {
//成员变量的位置
private static ThreadLocal<Connection> t1 = new ThreadLocal<>() ;//类一加载,创建线程
private static DataSource ds ; //数据源---->里面空的
//无参构造方法私有化
private JdbcUtils_WithDruid(){}
//静态代码块
static{
try {
//1)读取src下面的 德鲁伊的配置文件
//创建集合列表
Properties prop = new Properties() ;
//读取druid.properties
InputStream inputStream = JdbcUtils_WithDruid.class.getClassLoader().
getResourceAsStream("druid.properties");
//将资源文件输入流中的内容加载到属性列表中
prop.load(inputStream);
System.out.println(prop);
//2)将配置文件进行加载---DruidDataSource自动封装这些7个参数
//通过DruidDataSourceFactory 创建数据源
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
//定义方法----获取数据源的方法
public static DataSource getDataSource(){
return ds ;
}
//定义一个获取连接对象
public static Connection getConnection(){
Connection conn = null ;
try {
//现在模拟多线程场景
//1)从当前线程获取连接对象 ThreadLocal<Connection>----> Connection get()
conn = t1.get();
//2)判断当前线程中的连接对象如果为null,
if(conn==null){
//说明没有连接对象
//从连接池中获取连接对象
Connection connection = ds.getConnection();
//将连接池获取连接对象,绑定在当前线程中
//ThreadLocal<Connection>----> set(Connection conn)
t1.set(connection);
return connection ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn ;
}
//close()------------------>后面不用写了
/**
* 释放资源 针对DQL语句,select语句
* @param rs 释放结果集对象
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();//释放--->归还给连接池
//从当前线程中解绑
//ThreadLocl--->remove()
t1.remove();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 释放资源,针对DML语句:update,delete,insert操作的
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);//复用上面的close,第一个参数为null
}
public static void main(String[] args) {
DataSource dataSource = JdbcUtils_WithDruid.getDataSource();
System.out.println(dataSource);
Connection connection = JdbcUtils_WithDruid.getConnection();
System.out.println(connection);
}
}
Druid属性文件配置
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/库名 username=root password=******** initialSize=5? ? ? ? ? ? --->初始化数量 maxActive=10? ? ? ? ---->最大连接数量 maxWait=3000? ? ? ? ----->最大等待时间
package com.qf.druid_02;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* 德鲁伊Druid连接池的使用步骤:
* 1)导入druid的jar包
*
* com.alibaba.druid.pool.DruidDataSource---->最终实现了javax.sql.DataSource
* 2)准备好连接池的配置文件
* 配置好数据库的连接信息
* 以及一些连接池连接池参数
* 最大连接数量
* 空闲数量
* 超时时间...
*
* 3)创建连接池对象---->创建数据源DataSource接口不能实例化
* com.alibaba.druid.pool.DruidDataSource它具体类,可以创建
*
* 4)com.alibaba.druid.pool.DruidDataSourceFactory 连接池数据源工厂类---->静态方法
* 创建数据源
* protected DataSource createDataSourceInternal(Properties properties) throws Exception {
* DruidDataSource dataSource = new DruidDataSource();//创建数据源对象
* config(dataSource, properties); //将Properties 属性集合列表中的内容封装到数据源中
* return dataSource;
* }
*
*
* 连接池仅仅只是提供数据库连接对象的,最起码需要有数据库驱动jar包----操作数据库
*/
public class DruidDemo {
public static void main(String[] args) throws Exception {
//创建属性集合列表
Properties prop = new Properties() ;
System.out.println(prop);
//读取配置连接池文件
InputStream inputStream = DruidDemo.class.getClassLoader().
getResourceAsStream("druid.properties");
//将资源文件输入流对象加载到属性列表中
prop.load(inputStream);
System.out.println(prop);
//创建DruidDataSource对象
//protected DataSource createDataSourceInternal(Properties properties) throws Exception
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
System.out.println(dataSource) ;
//就可以连接池中获取连接对象
//DataSource---->getConnection()--->Connections
/* Connection connection = dataSource.getConnection();
Connection connection2 = dataSource.getConnection();
System.out.println(connection);
System.out.println(connection2);*/
for(int x = 1 ; x <=11 ; x++){
Connection connection = dataSource.getConnection();
if(x==3){
connection.close(); //不是释放,此时将第三个连接对象归还给连接池,有一个没有激活,第11个连接池对就可以获取到了
}
System.out.println(connection);
}
}
}
案例2
学生实体类
package com.qf.druid_02;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* 德鲁伊Druid连接池的使用步骤:
* 1)导入druid的jar包
*
* com.alibaba.druid.pool.DruidDataSource---->最终实现了javax.sql.DataSource
* 2)准备好连接池的配置文件
* 配置好数据库的连接信息
* 以及一些连接池连接池参数
* 最大连接数量
* 空闲数量
* 超时时间...
*
* 3)创建连接池对象---->创建数据源DataSource接口不能实例化
* com.alibaba.druid.pool.DruidDataSource它具体类,可以创建
*
* 4)com.alibaba.druid.pool.DruidDataSourceFactory 连接池数据源工厂类---->静态方法
* 创建数据源
* protected DataSource createDataSourceInternal(Properties properties) throws Exception {
* DruidDataSource dataSource = new DruidDataSource();//创建数据源对象
* config(dataSource, properties); //将Properties 属性集合列表中的内容封装到数据源中
* return dataSource;
* }
*
*
* 连接池仅仅只是提供数据库连接对象的,最起码需要有数据库驱动jar包----操作数据库
*/
public class DruidDemo {
public static void main(String[] args) throws Exception {
//创建属性集合列表
Properties prop = new Properties() ;
System.out.println(prop);
//读取配置连接池文件
InputStream inputStream = DruidDemo.class.getClassLoader().
getResourceAsStream("druid.properties");
//将资源文件输入流对象加载到属性列表中
prop.load(inputStream);
System.out.println(prop);
//创建DruidDataSource对象
//protected DataSource createDataSourceInternal(Properties properties) throws Exception
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
System.out.println(dataSource) ;
//就可以连接池中获取连接对象
//DataSource---->getConnection()--->Connections
/* Connection connection = dataSource.getConnection();
Connection connection2 = dataSource.getConnection();
System.out.println(connection);
System.out.println(connection2);*/
for(int x = 1 ; x <=11 ; x++){
Connection connection = dataSource.getConnection();
if(x==3){
connection.close(); //不是释放,此时将第三个连接对象归还给连接池,有一个没有激活,第11个连接池对就可以获取到了
}
System.out.println(connection);
}
}
}
Druid封装工具类
package com.qf.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 加入连接池Druid工具类的封装
* 模拟真实场景---->高并发情况下,每一个线程的连接对象应该使用自己的连接对象---->
* 来自于连接池! jdk提供了这个类ThreadLocal<Connection>
*/
public class JdbcUtils_WithDruid {
//成员变量的位置
private static ThreadLocal<Connection> t1 = new ThreadLocal<>() ;//类一加载,创建线程
private static DataSource ds ; //数据源---->里面空的
//无参构造方法私有化
private JdbcUtils_WithDruid(){}
//静态代码块
static{
try {
//1)读取src下面的 德鲁伊的配置文件
//创建集合列表
Properties prop = new Properties() ;
//读取druid.properties
InputStream inputStream = JdbcUtils_WithDruid.class.getClassLoader().
getResourceAsStream("druid.properties");
//将资源文件输入流中的内容加载到属性列表中
prop.load(inputStream);
System.out.println(prop);
//2)将配置文件进行加载---DruidDataSource自动封装这些7个参数
//通过DruidDataSourceFactory 创建数据源
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
//定义方法----获取数据源的方法
public static DataSource getDataSource(){
return ds ;
}
//定义一个获取连接对象
public static Connection getConnection(){
Connection conn = null ;
try {
//现在模拟多线程场景
//1)从当前线程获取连接对象 ThreadLocal<Connection>----> Connection get()
conn = t1.get();
//2)判断当前线程中的连接对象如果为null,
if(conn==null){
//说明没有连接对象
//从连接池中获取连接对象
Connection connection = ds.getConnection();
//将连接池获取连接对象,绑定在当前线程中
//ThreadLocal<Connection>----> set(Connection conn)
t1.set(connection);
return connection ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn ;
}
//close()------------------>后面不用写了
/**
* 释放资源 针对DQL语句,select语句
* @param rs 释放结果集对象
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(ResultSet rs, Statement stmt, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();//释放--->归还给连接池
//从当前线程中解绑
//ThreadLocl--->remove()
t1.remove();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
/**
* 释放资源,针对DML语句:update,delete,insert操作的
* @param stmt 释放执行对象
* @param conn 释放连接对象
*/
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);//复用上面的close,第一个参数为null
}
public static void main(String[] args) {
DataSource dataSource = JdbcUtils_WithDruid.getDataSource();
System.out.println(dataSource);
Connection connection = JdbcUtils_WithDruid.getConnection();
System.out.println(connection);
}
}
学生数据访问接口
package com.qf.dao;
import com.qf.pojo.Student;
import java.sql.SQLException;
import java.util.List;
/**
* 针对学生的数据访问接口
*
* DAO----> Data Access Object:数据访问对象----->jdbc的操作
* service---->业务服务层-----> 业务逻辑判断--if...else...一些具体的业务上的代码
*/
public interface StudentDao {
/**
* 查询所有学生
* @return 返回的学生列表,里面记录每一个学生实体
*/
List<Student> findAll() throws SQLException;
/**
* 模糊查询
* @param name 查询的姓名
* @return 返回学生列表
*/
List<Student> findStudentByName(String name) throws SQLException;
/**
* 通过学生编号查询学生实体
* @param id 学生编号
* @return 返回学生实体
*/
Student findStudentById(int id) throws SQLException;
/**
* 添加学生
* @param student 学生实体
* @return 返回值,影响的行数
*/
int registerStudent(Student student) throws SQLException;
}
学生数据访问接口实现类
package com.qf.dao.impl;
import com.qf.dao.StudentDao;
import com.qf.pojo.Student;
import com.qf.utils.JdbcUtils;
import com.qf.utils.JdbcUtils_WithDruid;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 针对学生的数据访问接口实现
*/
public class StudentDaoImpl implements StudentDao {
/**
* 查询所有学生
* @return 返回的学生列表,里面记录每一个学生实体
*/
@Override
public List<Student> findAll() throws SQLException {
//JDBC操作
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "select * from student" ;
//获取预编译对象
PreparedStatement ps = conn.prepareStatement(sql);//发送sql到数据库
//直接查询
ResultSet rs = ps.executeQuery();
//创建一个集合
List<Student> list = new ArrayList<>() ;
//声明学生变量
Student s =null ;
//遍历结果集
while(rs.next()){
//创建学生实体
s = new Student() ;
//封装学生数据
s.setId(rs.getInt("id"));
s.setName(rs.getString("name"));
s.setAge(rs.getInt("age"));
s.setGender(rs.getString("gender"));
s.setAddress(rs.getString("address"));
//将学生对象添加集合中
list.add(s) ;
}
//释放资源
JdbcUtils.close(rs,ps,conn);
return list;
}
/**
* 模糊查询
* @param name 查询的姓名
* @return 返回学生列表
*/
@Override
public List<Student> findStudentByName(String name) throws SQLException {
//JDBC操作
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "select * from student where name like ?" ;//参数化的sql
//获取预编译对象
PreparedStatement ps = conn.prepareStatement(sql);//发送sql到数据库,将编译sql存储到预编译对象中
//参数赋值
ps.setString(1,name);//测试的时候 name就是 '%王%'
//直接查询
ResultSet rs = ps.executeQuery();
//创建一个集合
List<Student> list = new ArrayList<>() ;
//声明学生变量
Student s = null ;
while(rs.next()){
s = new Student() ;
//封装学生数据
//封装学生数据
s.setId(rs.getInt("id"));
s.setName(rs.getString("name"));
s.setAge(rs.getInt("age"));
s.setGender(rs.getString("gender"));
s.setAddress(rs.getString("address"));
//将学生对象添加集合中
list.add(s) ;
}
//释放资源
JdbcUtils.close(rs,ps,conn);
return list;
}
/**
* 通过学生编号查询学生实体
* @param id 学生编号
* @return 返回学生实体
*/
@Override
public Student findStudentById(int id) throws SQLException {
//获取连接
Connection conn = JdbcUtils.getConnection();
//参数化的sql
String sql = "select * from student where id = ?" ;
//获取预编译对象并同时将参数化的sql发送给数据库
PreparedStatement ps = conn.prepareStatement(sql);
//参数赋值
ps.setInt(1,id);
Student s = null ;
//执行查询
ResultSet rs = ps.executeQuery();
while(rs.next()){
//创建学生对象
s = new Student() ;
//封装数据
s.setId(rs.getInt("id"));
s.setName(rs.getString("name"));
s.setAge(rs.getInt("age"));
s.setGender(rs.getString("gender"));
s.setAddress(rs.getString("address"));
}
//释放资源
JdbcUtils.close(rs,ps,conn);
return s;
}
/**
* 添加学生
* @param student 学生实体
* @return 返回值,影响的行数
*/
@Override
public int registerStudent(Student student) throws SQLException {
//获取连接
// Connection conn = JdbcUtils.getConnection();
//直接使用最终的工具类
Connection conn = JdbcUtils_WithDruid.getConnection();
//参数化的sql
String sql = "insert into student(name,age,gender,address) values(?,?,?,?)" ;
//获取预编译对象并同时将参数化的sql发送给数据库
PreparedStatement ps = conn.prepareStatement(sql);
//参数赋值
ps.setString(1,student.getName());
ps.setInt(2,student.getAge());
ps.setString(3,student.getGender());
ps.setString(4,student.getAddress());
//执行更新
int count = ps.executeUpdate();
//释放资源
JdbcUtils.close(ps,conn);
return count;
}
}
针对学生数据接口进行测试?
package com.qf.test;
import com.qf.dao.StudentDao;
import com.qf.dao.impl.StudentDaoImpl;
import com.qf.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
/**
* 针对学生的接口进行单元测试
*/
public class StudentTest {
private StudentDao sd ;
@Before
public void init(){
sd = new StudentDaoImpl() ;
}
//测试查询所有
@Test
public void testFindAll() throws SQLException {
List<Student> students = sd.findAll();
if(students!=null){
for(Student s:students){
System.out.println(s);
}
}
}
//测试模糊查询
@Test
public void testFindStudentByName() throws SQLException {
List<Student> students = sd.findStudentByName("%王%") ;
if(students!=null){
for(Student s:students){
System.out.println(s);
}
}
}
//测试通过学生编号查询指定的学生
@Test
public void testFindStudentById() throws SQLException {
Student student = sd.findStudentById(7);
if(student!=null){
System.out.println(student);
}
}
//测试添加学生信息
@Test
public void testAddStudent() throws SQLException {
//创建学生对象
Student student = new Student() ;
student.setName("李帅") ;
student.setAge(25);
student.setGender("男");
student.setAddress("灞桥") ;
sd.registerStudent(student) ;
}
}
Dbutils
Apache? 提供的工具类库? commons-Dbutils? ,针对原始JDBC操作七大步骤的简易封装, 简化代码书写量,提高开发效率
导入common-dbutils.jar包
Apache-dbutils官网
关于DBuitls三个常用的实现类,以及使用
BeanListHandler<T>: 将数据库查询的多条数据记录---->封装list<T>集合中
BeanHandler<T>:? ? ? ?将数据查询的某条信息----->封装某个实体类中
ScalarHandler<T>:? 将数据库查询的记录--->封装到Object类中--->返回单行单列的数据(聚合函数)---->查询总记录数
案例3
配置属性文件
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/库名 username=root password=********* initialSize=5 maxActive=10 maxWait=3000
dbutils封装druid(德鲁伊)工具类?
package com.qf.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DruidJdbcUtils {
//多个用户同时访问数据库
//模拟线程
private static ThreadLocal<Connection> tl = new ThreadLocal<>() ;
private static DataSource ds ;
//无参构造私有化
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//1)读取配置文件
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//2)创建属性集合列表Properties ---将资源文件所在的输入流的内容加载到属性列表中
Properties prop = new Properties() ;
prop.load(inputStream);
//3)通过DruidDataSourceFactory:数据源工厂创建DataSource 连接池(数据源)
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取数据源
public static DataSource getDataSource(){
return ds ;
}
//获取连接对象
public static Connection getConnection(){
try {
//1)从当前线程中获取连接对象
Connection conn = tl.get();
if(conn == null){
//从DataSource 连接池获取连接对象
conn = ds.getConnection() ;
//在将conn连接对象绑定当前线程中
tl.set(conn);
}
return conn ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
//关闭资源----不需要了--- Commons-Dutils.jar包--->QueryRunner---执行对象--->会自动释放资源
public static void main(String[] args) {
System.out.println(DruidJdbcUtils.getDataSource());
Connection connection = DruidJdbcUtils.getConnection();
System.out.println(connection);
}
}
学生实体类
package com.qf.pojo;
/**
* 学生类
*/
public class Student {
private int id ;
private String name ;
private int age ;
private String gender;
private String address ;
public Student() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
'}';
}
}
学生数据访问接口
package com.qf.dao;
import com.qf.pojo.Student;
import java.sql.SQLException;
import java.util.List;
/**
* 针对学生的数据访问接口
*/
public interface StudentDao {
/**
* 查询所有学生
* @return 返回学生列表
*/
List<Student> findAll() throws SQLException;
/**
* 通过学生编号查询学生信息
* @param id 学生编号
* @return 返回学生实体
*/
Student findStudentById(int id) throws SQLException;
/**
* 添加学生
* @param student 学生实体
* @return 返回影响的行数
*/
int addStudent(Student student) throws SQLException;
/**
* 查询总记录数
* @return 返回的总条数信息
*/
int selectTotalCount() throws SQLException;
/**
* 修改学生信息
* @param student 学生实体
* @return 返回影响的行数
*/
int updateStudent(Student student) throws SQLException;
}
学生数据访问接口实现类
package com.qf.dao.impl;
import com.qf.dao.StudentDao;
import com.qf.pojo.Student;
import com.qf.utils.DruidJdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.SQLException;
import java.util.List;
/**
* 针对学生的数据访问接口
*
*
* //common-Dbutils的使用步骤
* 1)导入dbutils.jar包
* 2)创建执行对象
* public QueryRunner(DataSource ds)----->自动提交方式 (前期都用这个)
* public QueryRunner() ----->空参构造---手动提交(牵扯事务的是用)
* 3)准备sql语句 update/delete/insert /select...
* 4)执行
* QueryRunner针对update/delete/insert----通用方法
* int update(String sql,Object[]...params):参数1:sql语句 参数2:就是可变参数,给占位符赋值 (属于自动提交更新)
* int update(Connection conn,String sql,Object[]...params) 更新方法
*
*
* //通用的查询方法
* public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
* //参数1:sql语句
* //参数2:结果集的处理---接口--->需要子实现类
* BeanListHandler<T> :将数据库查询的多条记录--->封装List<T>集合中
* BeanHandler<T>:将数据库中查询的某条记录---->封装某个实体类中
* ScalarHandler<T>:将数据库查询的记录--->封装Object类中---返回的数据单行单列的数据(聚合函数)---->查询总记录数
* //参数3:可变参数,在select语句如果有条件查询,传入条件值;如果没有参数,不需要传!
*
*
*
*
*/
public class StudentDaoImpl implements StudentDao {
/**
* 查询所有学生
* @return 返回学生列表
*/
@Override
public List<Student> findAll() throws SQLException {
// public QueryRunner(DataSource ds)
//1)创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//2)准备sql语句
String sql = "select * from student" ;
//3)执行查询
// BeanListHandler<T> :将数据库查询的多条记录--->封装List<T>集合中
//public BeanListHandler(Class<T> type)
List<Student> list = qr.query(sql, new BeanListHandler<Student>(Student.class));
return list;
}
/**
* 通过学生编号查询学生信息
* @param id 学生编号
* @return 返回学生实体
*/
@Override
public Student findStudentById(int id) throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select * from student where id = ?" ;
//查询---执行
// BeanHandler<T>:将数据库中查询的某条记录---->封装某个实体类中
//public BeanHandler(Class<T> type) :参数就是当前存储类型的字节码文件
Student student = qr.query(sql, new BeanHandler<Student>(Student.class), id);
return student;
}
/**
* 添加学生
* @param student 学生实体
* @return 返回影响的行数
*/
@Override
public int addStudent(Student student) throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "insert into student(name,age,gender,address) values(?,?,?,?)" ;
//更新操作
// int update(String sql,Object[]...params):参数1:sql语句 参数2:就是可变参数,给占位符赋值 (属于自动提交更新)
int count = qr.update(sql,
student.getName(),
student.getAge(),
student.getGender(),
student.getAddress());
return count;
}
/**
* 查询总记录数
* @return 返回的总条数信息
*/
@Override
public int selectTotalCount() throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select count(id) from student" ;
//执行查询
// ScalarHandler<T>:将数据库查询的记录--->封装Object类中---返回的数据单行单列的数据(聚合函数)
// ---->查询总记录数
// public ScalarHandler() 空参构造
Object obj = qr.query(sql, new ScalarHandler<>());
//Object--->String---->Integer---->int
// String str = String.valueOf(obj);
// int totalCount = Integer.parseInt(str);
int totalCount = Integer.parseInt(String.valueOf(obj));
return totalCount;
}
/**
* 修改学生信息
* @param student 学生实体
* @return 返回影响的行数
*/
@Override
public int updateStudent(Student student) throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "update student set name = ?,age=?,gender=?,address = ? where id = ?" ;
//执行更新
int count = qr.update(sql,
student.getName(),
student.getAge(),
student.getGender(),
student.getAddress(),
student.getId());
return count;
}
}
学生数据测试类
package com.qf.test;
import com.qf.dao.StudentDao;
import com.qf.dao.impl.StudentDaoImpl;
import com.qf.pojo.Student;
import org.junit.Before;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
/**
* 单元测试
*/
public class Dbutils_Test {
private StudentDao sd ;
@Before
public void init(){
sd = new StudentDaoImpl() ;
}
//测试查询所有
@Test
public void testFindAll() throws SQLException {
List<Student> students = sd.findAll();
if(students!=null){
for(Student s:students){
System.out.println(s);
}
}
}
//测试指定的学生信息
@Test
public void testFindStudentById() throws SQLException {
Student student = sd.findStudentById(1);
System.out.println(student);
}
//测试添加学生
@Test
public void testAddStudent() throws SQLException {
//创建学生
Student s = new Student() ;
s.setName("刘亦菲") ;
s.setAge(30);
s.setGender("女");
s.setAddress("西安市");
int count = sd.addStudent(s);
System.out.println(count);
}
//测试查询总记录数
@Test
public void testSelectTotalCount() throws SQLException {
int count = sd.selectTotalCount();
System.out.println(count);
}
//测试修改学生
@Test
public void testUpdate() throws SQLException {
Student s = new Student() ;
s.setName("梁朝伟") ;
s.setAge(50);
s.setGender("男") ;
s.setAddress("香港");
s.setId(11);
int count = sd.updateStudent(s) ;
System.out.println(count);
}
}
|