学习JavaWeb第十天
JDBC事务
-
事务的概念
- 一组预编译的数据库语句,要么全部执行成功,要么全部执行失败
-
事务的ACID四个原则(四个特性)
- 原子性(atomicity)
一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。 - 一致性(consistency)
事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 状态一致 - 隔离性(isolation)
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 - 持久性(durability)
持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
隔离级别
- 原子性(atomicity)
JDBC事务的操作
- con.setAutoCommit(false) 自动提交
- true
自动提交 - false
手动提交
- true
事务之间的隔离级别
- Read uncommitted读未提交,就是一个事务可以读取另一个未提交事务的数据。
- Read committed 读提交,就是一个事务要等另一个事务提交后才能读取数据。
- Repeatable read 重复读(MySQL默认的级别),就是在开始读取数据(事务开启)时,不再允许修改操作
- Serializable序列化 是最高的事务隔商级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数掘库性能,一般不使用。
出现的问题:
1.脏读:读取到错误的数据(在修改的情况)
2.不可以重复读取:两次读取到不同的数据(在修改的情况下)
3.幻读:两次读取到不同的数据(插入数据的条件下)
解决这些问题:就是让一个事务进行操作的时候,另一个事务不操作(互斥锁)
mysql的事务操作
事务的执行原理:只有commit之后才能把临时日志文件的数据提交到数据库里,开启事务之后,都会先保存到临时日志文件里,rollback会进行日志的清空
-- mysql 数据默认是自动提交:默认为1
-- 修改为手动提交:
set autocommit=0
-- 开启事物:
start transaction
-- 提交事物:
commit
-- :回滚事务(最初的状态)
rollback
-- 设置回滚点:lm是回滚点的名称
savepoint lm;
-- 回滚到具体的哪一步
rollback to lm;
mysql的事务操作(java)
- setAutoCommit(false);
开启事务 - commit
提交事务 - rollback
回滚事务
例子:
/*
* // 描述 s q l 回滚过程,要么一并执行,要么一并不执行
*/
@Test // 单元测试模式
public void describe() throws Exception {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/day10", "root", "root");
// 设置是否自动提交,手动提交false or 自动提交 true
connection.setAutoCommit(false);
// 模拟转账过程
try {
// 编写sql语句
String sql = "update user set moeny = moeny-? where name =?";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
// 设置占位符内容
prepareStatement.setInt(1, 500);
prepareStatement.setString(2, "李四");
// 获取结果
int update = prepareStatement.executeUpdate();
System.out.println("修改的行数有" + update);
// 模拟断电过程 发生异常
int i = 1 / 0;
// 编写sql语句
String sql1 = "update ? set moeny = moeny+? where name =?";
PreparedStatement prepareStatement1 = connection.prepareStatement(sql1);
prepareStatement1.setInt(1, 500);
prepareStatement1.setString(2, "张三");
int update1 = prepareStatement1.executeUpdate();
System.out.println("修改的行数有" + update1);
// 手动提交
connection.commit();
// 关闭资源
prepareStatement1.close();
prepareStatement.close();
connection.close();
} catch (Exception e) {
// sql语句不执行完整,sql语句回滚,表内容不变
connection.rollback();
}
}
oracle&mysql事务
- mysql事务默认是自动提交
- oracle事务默认是手动提交
数据库连接池
- 连接池产生的原因
- 就是因为 每次对数据库进行CRUD操作都需要 获取连接,执行完操作后。都需要销毁连接 频繁的获取 和销毁连接资源 会造成大量的内存开销和影响程序的执行效率
- 市面上常用的连接池有:1.dbcp 2.c3p0 3.阿里(druid)
所有的连接池都是由厂商提供
dbcp在apache下,是服务器 tomcat的内置连接池
特点:dbcp速度快,但安全性低(可能会出现丢失数据)
c3p0速度慢,但安全性高(重点)
关于配置文件的加载:
- 方式1.
使用类加载ClassLoader加载src的资源(固定写法)
获得ClassLoader固定写法:当前类.class.getClassLoader().getResourceAsStream(“dbcp.properties”) - 方式2
加载当前类同包下的资源,如果需要从src开始必须填写 “/”开头
当前类 .class.getResourceAsStream("/jdbc.properties"); - 方式3
使用文件输入来读取
FileInputStream inputStream = new FileInputStream(“src/dbcp.properties”);
三大数据库连接池:DBCP、C3P0、Druid
DBCP使用步骤:
- DBCP连接池
-
使用步骤:
-
1、导入数据库驱动jar包、导入连接池jar包dbcp
commons-dbcp-1.4.jar
mysql-connector-java-5.1.40-bin.jar
到lib文件目录下,并添加path -
2、定义配置文件:dbcp.properties
- 放置到src文件夹目录下
-
3、加载读取配置文件properties 使用反射读取配置文件时,配置文件需要与类放于同src目录下
-
4、调用BasicDataSourceFactory的createDataSource(properties)方法读取配置文件,
返回的是一个DataSource接口 -
5、调用DataSource的getConnection()方法获取连接对象,返回的是Connection类对象
-
6、编写sql语句
-
7、创建数据库对象
Statement createStatement = con.createStatement();
创建查询语句 ResultSet executeQuery = createStatement.executeQuery(sql);
或者
PreparedStatement prepareStatement = con.prepareStatement(sql); -
8、返回的是结果集或受影响的行数
ResultSet executeQuery = createStatement.executeQuery(sql);
或者
ResultSet resultSet = prepareStatement.executeQuery(); -
9、关闭资源 ResultSet PreparedStatement
Connection的不用关闭
-
dbcp.properties 文件:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day10
username=root
password=root
initialSize=6
maxActive=6
代码示例:
@Test
public void select() throws Exception {
FileInputStream inputStream = new FileInputStream("src/dbcp.properties");
// InputStream inputStream=TestDBCP.class.getClassLoader().getResourceAsStream("dbcp.properties");
// InputStream inputStream = TestDBCP.class.getResourceAsStream("/dbcp.properties");
Properties properties=new Properties();
properties.load(inputStream);
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
String sql = "select * from user";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
ResultSet query = prepareStatement.executeQuery();
while (query.next()) {
String name = query.getString("name");
int id = query.getInt("id");
double moeny = query.getDouble("moeny");
System.out.println(name+"\t"+id+"\t"+moeny);
}
connection.commit();
query.close();
prepareStatement.close();
prepareStatement.close();
}
.
C3P0使用步骤:
C3P0连接池
- 使用C3P0连接池示例步骤
-
1、导入数据库驱动jar包、导入连接池jar包C3P0
mysql-connector-java-5.1.40-bin.jar
c3p0-0.9.2.1.jar -
2、配置C3P0文件:
- 第一种格式:c3p0.properties
- 第二种格式:c3p0-config.xml
-
3、调用ComboPooledDataSource的 构造方法 (properties)读取配置文件,
无参构造是默认读取c3p0.properties文件或者是 c3p0-config.xml文件
有参构造是读取自定义的c3p0-config.xml文件下选定的内容
返回的是一个ComboPooledDataSource 对象 -
4、调用ComboPooledDataSource的getConnection()方法获得连接对象
返回的是Connection类对象 -
5、编写 sql 语句
-
6、创建数据库对象
Statement createStatement = con.createStatement();
创建查询语句 ResultSet executeQuery = createStatement.executeQuery(sql);
或者
PreparedStatement prepareStatement = con.prepareStatement(sql); -
7、返回的是结果集或受影响的行数
ResultSet executeQuery = createStatement.executeQuery(sql);
或者
ResultSet resultSet = prepareStatement.executeQuery();
… -
8、关闭资源 ResultSet PreparedStatement
Connection的不用关闭
-
配置C3P0文件,
c3p0.properties文件:
#c3p0
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test0820?useUnicode=true&characterEncoding=utf-8
c3p0.user=root
c3p0.password=root
c3p0.acquireIncrement=3
c3p0.idleConnectionTestPeriod=60
c3p0.initialPoolSize=10
c3p0.maxIdleTime=60
c3p0.maxPoolSize=150
c3p0.maxStatements=100
c3p0.minPoolSize=5
c3p0-config.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///day08</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">200</property>
</default-config>
<named-config name="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///day10</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">200</property>
</named-config>
<named-config name="oracle">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///day08</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">20</property>
<property name="minPoolSize">5</property>
<property name="maxStatements">200</property>
</named-config>
</c3p0-config>
代码示例:
@Test
public void select() throws Exception {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("mysql");
Connection connection = comboPooledDataSource.getConnection();
connection.setAutoCommit(false);
String sql = "select * from user";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
ResultSet query = prepareStatement.executeQuery();
while (query.next()) {
int id = query.getInt("id");
String name = query.getString("name");
double moeny = query.getDouble("moeny");
System.out.println(id+"\t"+name+"\t"+moeny);
}
query.close();
prepareStatement.close();
}
Durid使用步骤:
Durid连接池
- 使用步骤:
-
1、导包druid连接池jar包和导入数据库驱动jar包
druid-1.0.9.jar
mysql-connector-java-5.1.40-bin.jar -
2、配置durid配置文件,配置properties 加载properties
-
3、调用DruidDataSourceFactory的createDataSource(properties),读取properties,返回的是DataSource对象
-
4、调用DataSource的getConnection()方法
返回的是Connection类对象 -
5、编写 sql 语句
-
6、创建数据库对象
Statement createStatement = con.createStatement();
创建查询语句 createStatement.executeQuery(sql);
或者
PreparedStatement prepareStatement = con.prepareStatement(sql); -
7、返回的是结果集或受影响的行数
ResultSet executeQuery = createStatement.executeQuery(sql);
或者
ResultSet resultSet = prepareStatement.executeQuery();
… -
8、关闭资源 ResultSet PreparedStatement
Connection的不用关闭
代码示例:
-
@Test
public void alter() throws Exception {
Properties properties = new Properties();
InputStream inputStream = TestDurid.class.getClassLoader().getResourceAsStream("durid.properties");
properties.load(inputStream);
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();
String sql = "update user set moeny=8888 where id=?";
PreparedStatement prepareStatement = connection.prepareStatement(sql);
prepareStatement.setInt(1, 3);
int update = prepareStatement.executeUpdate();
prepareStatement.close();
System.out.println("受影响的行数有:"+update);
}
DBUtils工具(框架)
-
概述
传统JDBC开发过程中,无论是增删该数据,还是查询数据都特别麻烦,如果使用连接池就更加麻烦,所以DBUTILS这个工具目的就是简化CRUD操作。- 一般用于单张表操作,用于多张表操作过于复杂。
-
DML 数据操纵语言,表中数据的增删改
本类为测试dbutils类
- 使用步骤:
1、导入jar包 dbutils 驱动包 c3p0连接池jar包。
比如:C3p0 c3p0-0.9.2.1.jar
mysql-connector-java-5.1.40-bin.jar
commons-dbutils-1.4.jar
2、从连接池里面获取dataSource:,new ComboPooledDataSource()
3、调用QueryRunner的有参构造方法new QueryRunner(dataSource)
4、调用queryRunner的方法 查询方法:query() 修改的方法:update()
QueryRunner:核心对象,提供了一些增加、删除、修改、查询的方法
- 增加、删除、修改的方法:qr.update()
- 第一个参数:连接对象(可有可无)
- 第二个参数:sql语句
- 第三个参数:占位符的值
- 查询的方法:qr.query()
- 查询:第一个参数是sql 语句,
- 第二参数BeanListHandler(User.class),
- 表示返回的是一个结果集,就是集合,
- 这个集合的泛型对应的就是其类名(列名与属性名都必须一样(通过反射来查找对应的属性)
查询
- 探索结果集 返回对象的,需要定义于表中变量一样名称的字段及其类型
- 多个对象记录 BeanListHandler 返回的是List 返回多个对象
- 多个对象 ArrayListHandler 返回的是List<Object[]>
- 单个对象 BeanHandler 返回的是第一行数据, 把第一行数据封装成了一个map对象
- 单个对象 ArrayHandler 返回的是数组Object[] 无论数据多少、只返回第一行
- 统计条数 ScalarHandler 返回的总记录数。注意点:一定要先使用long类型接收,再进行转化
- 查询某列 ColumnListHandler 返回的是List 返回的是具体的一列,需要指明那一列
- 单个对象 MapHandler 返回的是Map<String, Object> map的键就是 数据库列名称 值就是 列对应的内容
- 多行对象记录 MapListHandler 把每一行封装成一个map对象 这个适用于查询所有对象
修改
- int row = queryRunner.update(sql,4);
返回的是受影响的行数
代码示例:
//new BeanListHandler<User>(User.class) 返回的是一个存储对象的集合List
@Test
public void queryBeanList() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user";
List<User> query = queryRunner.query(sql, new BeanListHandler<User>(User.class));
for (User user : query) {
System.out.println(user);
}
}
// new BeanHandler<User>(User.class) 返回的是一个 是单个对象 如果有多个对象也只会返回第一个对象
@Test
public void queryBean() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user where id=?";
User query = queryRunner.query(sql, new BeanHandler<User>(User.class),3);
System.out.println(query);
}
//new ArrayListHandler() 返回的是一个存储数组对象的集合
@Test
public void queryArrayList() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user";
List<Object[]> query = queryRunner.query(sql, new ArrayListHandler());
for (Object[] objects : query) {
System.out.println(Arrays.toString(objects));
}
}
//new ArrayHandler() 返回的是一个对象数组 但无论数据多少、只返回第一行
@Test
public void queryArray() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user";
Object[] query = queryRunner.query(sql, new ArrayHandler());
System.out.println(Arrays.toString(query));
}
//new ScalarHandler() 返回的是一个总记录条数,sql语句需要改为统计条数的语句
@Test
public void queryScalar() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select count(*) from user";
Long query =(Long) queryRunner.query(sql, new ScalarHandler());
System.out.println(query.intValue());
}
//new ColumnListHandler("name") 返回的是具体的一列,需要指明那一列
@Test
public void queryColumnList() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user";
List<Object> query = queryRunner.query(sql, new ColumnListHandler("moeny"));
for (Object object : query) {
System.out.println(object);
}
}
//new MapListHandler() 返回的是所有对象装载着Map集合的List集合 ,需要遍历,Map集合里面装载着所有对象Object
@Test
public void queryMapList() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user";
List<Map<String,Object>> query = queryRunner.query(sql, new MapListHandler());
/*
* for (Map<String, Object> map : query) { Set<String> keySet = map.keySet();
* for(String set :keySet) { System.out.println(map.get(set)); }
*
* }
*/
for (Map<String, Object> map : query) {
Set<Entry<String,Object>> entrySet = map.entrySet();
for(Entry<String,Object> entry :entrySet) {
System.out.println(entry.getKey()+"\t"+entry.getValue());
}
}
}
//new MapHandler() 返回的是一个对象的Map集合,需要遍历,而且顺序不一样 遇到多个对象也只会返回一个对象的Map集合
@Test
public void queryMap() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
String sql="select * from user";
Map<String, Object> query = queryRunner.query(sql, new MapHandler());
Set<Entry<String,Object>> entrySet = query.entrySet();
for (Entry<String, Object> entry : entrySet) {
System.out.println(entry.getKey()+"\t"+entry.getValue());
}
}
//queryRunner.update(sql,88,2); 第一个为sql语句,第二个及以后的为占位符内容
@Test
public void update() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");
QueryRunner queryRunner = new QueryRunner(dataSource);
/*
* 增加
* String sql = "insert into user values(?,?,?),(?,?,?)"; int update =
* queryRunner.update(sql,4,"老刘",3000,6,"老毛",2322);
*/
String sql = "delete from user where id=?";
int update = queryRunner.update(sql,4);
/*
* 修改
* String sql = "update user set moeny=? where id=?"; int update =
* queryRunner.update(sql,88,2);
*/
System.out.println("受影响的行数有:"+update);
}