jdbc事务处理和连接池
JDBC:
* JDBC概念:Java DataBase Connectivity(Java数据库连接)
SUN公司提供的一组连接数据库API.
* JDBC开发步骤:
* 1.注册驱动.
* 2.获得连接.
* 3.执行SQL.
* 4.释放资源.
* JDBC入门案例:
* JDBC的API详解:
* DriverManager:
* 管理驱动:
* Class.forName(“com.mysql.jdbc.Driver”);
* 获得连接:
* getConnection(String url,String username,String password);
* Connection:
* 创建执行SQL的对象:
* createStatement();
* prepareStatement(String sql);
* 管理事务:
* setAutoCommit(boolean flag);
* commit();
* rollback();
* Statement:
* 执行SQL语句
* executeQuery(String sql);
* executeUpdate(String sql);
* execute(String sql);
* 执行批处理
* addBatch(String sql);
* executeBatch();
* clearBatch();
* ResultSet:
* 结果集:
* 默认只能向下不可以修改.
* 多条记录:
while(rs.next()){
rs.getXXX();
}
* 一条记录:
if(rs.next()){
}
* 滚动结果集:(了解)
JDBC开发CRUD的操作:
* 资源释放:
* Connection的资源是非常稀有的,应该做到晚创建,早释放!
* 抽取工具类:
* 提取了一个配置文件db.properties
EE开发中的DAO模式:
* DAO模式主要用来解决持久层的问题!
* 操作数据库!
DAO模式编写了一个登陆案例:
* 页面---->Servlet---->UserService---->UserDao
* 演示了SQL注入漏洞:
* 解决:
* PreparedStatement:对SQL进行预编译!
大文件读写:
批处理:
1.1 JDBC中事务管理:
1.1.1 事务:
事务:指的是逻辑上一组操作!一组操作要么全都成功,要么全都失败!
1.1.2 MYSQL数据库中操作事务
MYSQL的数据库的事务是自动提交的!!!Oracle不是自动提交的!!!
MySQL数据库,写一个SQL就是一个事务!!!
创建一个表:
create database day18;
use day18;
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
insert into account values (null,'美美',10000);
insert into account values (null,'小边',10000);
insert into account values (null,'冠希',10000);
insert into account values (null,'白鸽',10000);
insert into account values (null,'小白',10000);
MYSQL中管理事务:
有两种方式管理事务:
一、使用命令方式:
* start transaction; --- 开启事务
* commit; --- 事务提交
* rollback; --- 事务回滚
二、设置MYSQL数据库参数:
* show variables like '%commit%';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| autocommit | ON |
| innodb_commit_concurrency | 0 |
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
* 设置自动提交参数为OFF(0) ON(1)
set autocommit = 0;
1.1.3 JDBC中使用事务:
JDBC的事务
通过Connection对象中的
* setAutoCommit(false);
* commit();/rollback();
案例:
@Test
// 冠希给美美转账 1000元
public void demo1(){
Connection conn = null;
PreparedStatement stmt = null;
try{
// 获得连接
conn = JDBCUtils.getConnection();
// 管理事务:事务不是自动提交!
conn.setAutoCommit(false);
// 编写一个SQL语句
String sql = "update account set money = money + ? where name = ?";
// 预编译SQL
stmt = conn.prepareStatement(sql);
// 设置参数:
// 扣去冠希的1000元
stmt.setDouble(1, -1000);
stmt.setString(2, "冠希");
stmt.executeUpdate();
// 如果现在没有事务的保证:当扣除冠希的1000元之后,会发生什么情况?
// 这个时候 冠希的钱就被扣除了,但是美美没有收到钱!!!
// 所以多条SQL要由事务保证:事务就是保证逻辑上的一组操作要么全都成功,要么全都失败!
int a = 10 / 0;
// 为美美增加1000元
stmt.setDouble(1, 1000);
stmt.setString(2, "美美");
stmt.executeUpdate();
// 提交事务:
conn.commit();
}catch(Exception e){
// 异常发生了:
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.release(stmt, conn);
}
}
JDBC事务的保存点:(了解)
保存点的作用:回滚的时候不用回滚到最初始的状态!!!
Connection中有
* setSavepoint() --- 设置一个保存点
* rollback(SavePoint savePoint); --- 事务回滚到保存点的位置
@Test
// 小边跟美美说你借我1w,设置一个保存点,我还你10w.但是小边的账号余额不足.回滚到保存点的位置!
public void demo2(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try{
// 获得连接
conn = JDBCUtils.getConnection();
// 开启事务:
conn.setAutoCommit(false);
// 编写一个sql语句.
String sql = "update account set money = money + ? where name = ?";
// 预编译SQL
stmt = conn.prepareStatement(sql);
// 扣除美美的1w元
stmt.setDouble(1, -10000);
stmt.setString(2, "美美");
stmt.executeUpdate();
// 给小边增加1w元
stmt.setDouble(1, 10000);
stmt.setString(2, "小边");
stmt.executeUpdate();
// 设置一个保存点
Savepoint savePoint = conn.setSavepoint();
// 小边还给美美10w
stmt.setDouble(1, -100000);
stmt.setString(2, "小边");
stmt.executeUpdate();
stmt.setDouble(1, 100000);
stmt.setString(2, "美美");
stmt.executeUpdate();
sql = "select * from account where name = ?";
stmt = conn.prepareStatement(sql);
// 设置参数
stmt.setString(1, "小边");
rs = stmt.executeQuery();
if(rs.next()){
if(rs.getDouble("money")<0){
conn.rollback(savePoint);
System.out.println("上当了!!!");
}
}
// 事务提交
conn.commit();
}catch(Exception e){
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
}
1.1.4 事务的特性:(面试)
原子性:
* 强调的是事务的不可分割!
一致性:
* 事务执行的前后,数据的完整性保持一致!
隔离性:
* 一个事务的执行,不应该受到另一个事务的打扰!
持久性:
* 事务执行结束!数据就永久的保存数据库中!
如果不考虑事务的隔离性:引发哪些问题?
* 引发三种读的问题.
* 脏读 :一个事务读到了另一个事务未提交的数据!!!
* 不可重复读 :一个事务读到了另一个事务已经提交数据(另一个事务中做的是update操作),导致多次查询结果在一个事务中不一致!
* 虚读(幻读) :一个事务读到了另一个事务已经提交数据(另一个事务中做的是insert操作),导致在事务中做的统计的结果不一致!!
* 如何解决问题?
* 数据库提供了四个隔离级别解决三种读问题!!!
* read uncommitted :未提交读.那么脏读、不可重复读、虚读都是有可能发生!!
* read committed :已提交读.避免脏读。但是不可重复读和虚读是有可能发生!!
* repeatable read :重复读.避免脏读、不可重复读。但是虚读是有可能发生的!!
* serializable :串行的.避免脏读、不可重复读、虚读的发生!!
* 安全性:
read uncommitted < read committed < repeatable read < serializable
* 效率性:
read uncommitted > read committed > repeatable read > serializable
***** 数据库中使用的时候一般不会采用最低和最高的!!!
* MYSQL中使用的是repeatable read Oracle中read committed
通过命令查看事务的隔离级别:
* select @@tx_isolation;
设置数据库隔离级别:
* set session transaction isolation level 隔离级别;
演示脏读发生:
脏读:一个事务读到了另一个事务未提交的数据!!!
1.开启两个窗口A、B!分别连接到MYSQL数据库!
* 分别在两个窗口中查看隔离级别!
* select @@tx_isolation;
2.设置A窗口的隔离级别为read uncommitted
* set session transaction isolation level read uncommitted;
* 查看A窗口的隔离级别
* A:READ-UNCOMMITTED
* B:REPEATABLE-READ
3.两个窗口分别开启事务:
* start transaction;
4.在B窗口中完成转账的代码:但是事务不要提交!
* update account set money = money - 1000 where name = '冠希';
* update account set money = money + 1000 where name = '美美';
5.在A窗口中进行查询:
* select * from account;
* 发现钱已经到账了!!!脏读.(A窗口已经读到B窗口还没有提交的数据)
演示不可重复读的发生(避免脏读)
不可重复读:一个事务读到另一个事务已经提交的数据!(另一个事务update)导致当前的事务多次的查询结果不一致
* 避免脏读:设置隔离级别为read committed
1.开启两个窗口A、B!分别连接到MYSQL数据库!
* 分别在两个窗口中查看隔离级别!
* select @@tx_isolation;
2.在A窗口中设置数据库隔离级别为read committed
* set session transaction isolation level read committed;
* A:READ-COMMITTED
* B:REPEATABLE-READ
3.两个窗口分别开启事务:
* start transaction;
4.在B窗口完成转账代码:
* update account set money = money - 1000 where name = '冠希';
* update account set money = money + 1000 where name = '美美';
***** 先不提交事务!
5.在A窗口进行查询!
* select * from account;
* 这个时候钱没有到账!(避免脏读了 一个事务没有读到另一个事务未提交的数据)
6.在B窗口提交事务:
* commit;
7.在A窗口再次去查询!
* select * from account;
* 发现这次结果和上次的结果不一致(两次查询是在一个事务中的!):不可重复读.
演示避免不可重复读
1.开启两个窗口A、B!分别连接到MYSQL数据库!
* 分别在两个窗口中查看隔离级别!
* select @@tx_isolation;
2.设置A窗口的隔离级别:repeatable read
* set session transaction isolation level repeatable read;
* A:REPEATABLE-READ
* B:REPEATABLE-READ
3.两个窗口分别开启事务:
* start transaction;
4.在B窗口完成转账:
* update account set money = money - 1000 where name = '冠希';
* update account set money = money + 1000 where name = '美美';
事务没有提交
5.在A窗口查询:
* select * from account;
* 数据没有变化:避免脏读!
6.在B窗口提交事务:
* commit;
7.在A窗口再次查询:
* select * from account;
* 数据还是没有变化:避免了不可重复读!
演示隔离级别为serializable:
Serializable:串行的!可以避免脏读、不可重复读、虚读!
1.开启两个窗口A、B!分别连接到MYSQL数据库!
* 分别在两个窗口中查看隔离级别!
* select @@tx_isolation;
2.设置A窗口的隔离级别为 Serializable
* set session transaction isolation level Serializable;
* A:SERIALIZABLE
* B:REPEATABLE-READ
3.在两个窗口分别开启事务:
* start transaction;
4.在B窗口中插入一条记录:
* 事务不提交:
5.在A窗口中进行查询:
* select * from account;
* 屏幕卡住了:因为另一个事务没有提交.这个事务就不能执行,串行的!!!
1.1.5 JDBC中如何设置隔离级别
通过Connection对象的:
* setTransactionIsolation(int level) ;
Connection中有以下的常量:
static int TRANSACTION_READ_COMMITTED
指示不可以发生脏读的常量;不可重复读和虚读可以发生。
static int TRANSACTION_READ_UNCOMMITTED
指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。
static int TRANSACTION_REPEATABLE_READ
指示不可以发生脏读和不可重复读的常量;虚读可以发生。
static int TRANSACTION_SERIALIZABLE
指示不可以发生脏读、不可重复读和虚读的常量。
1.1.6 丢失更新:(扩展)
第一类丢失更新
A事务撤销时,把已提交的B事务的数据覆盖掉。这种错误会造成非常严重的后果。
第二类丢失更新
A事务提交时,把已提交的B事务的数据覆盖掉。这种错误会造成非常严重的后果。
解决丢失更新问题:
* 悲观锁:假设丢失更新一定存在!
* 使用的是数据库的一种锁机制:排他锁.
* 乐观锁:假设丢失更新不一定存在!
1.2 连接池
1.2.1 什么是连接池
连接池:就是一个装了很多连接一个容器.(内存中的一块空间,装了很多连接.)
1.2.2 使用连接池:
1.编写一个类 实现DataSource接口.
2.重写getConnection()的方法.
自定义连接池的代码:
public class MyDataSource implements DataSource{
private List<Connection> list = new ArrayList<Connection>();
// 提供一个构造方法:
public MyDataSource(){
// 创建3个连接
for (int i = 1; i <=3; i++) {
// 向list集合放入连接
list.add(JDBCUtils.getConnection());
}
}
// 从连接池中获得连接
public Connection getConnection() throws SQLException {
// 从连接池中获得连接.连接池的扩容
if(list.isEmpty()){
for (int i = 1; i <=3; i++) {
// 向list集合放入连接
list.add(JDBCUtils.getConnection());
}
}
Connection conn = list.remove(0);
return conn;
}
// 用完之后,不是销毁连接而是归还到连接池
public void addBack(Connection conn){
list.add(conn);
}
public PrintWriter getLogWriter() throws SQLException {
return null;
}
public void setLogWriter(PrintWriter out) throws SQLException {
}
public void setLoginTimeout(int seconds) throws SQLException {
}
public int getLoginTimeout() throws SQLException {
return 0;
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
public Connection getConnection(String username, String password)
throws SQLException {
return null;
}
}
****** 自定义连接池中在使用的使用!需要让程序员额外记住一些自定义的方法例如addBack();而且在构造的时候不能使用接口构造.
能不能有一种方式实现:使用接口构造连接池,而且在归还的时候不需要额外提供方法???
* 原来的Connection的close方法是将连接对象销毁!现在是否可以增强close方法,使方法的逻辑改变,归还连接而不是销毁连接?
* 增强Java中类的方法有几种方式?
* 继承 :
class Person{
public void run(){
System.out.println("跑步1000米...");
}
}
class SuperPerson extends Person{
@Override
public void run() {
// super.run();
System.out.println("跑步10000米...");
}
}
测试:
@Test
public void demo1(){
Person p = new SuperPerson();
p.run(); // 子类的方法.
}
***** 继承这种增强某个类的方法使用条件的:
* 必须能够控制这个类的构造!
* 装饰者模式 :
代码:
interface Bird{
public void fly();
}
// 被增强的类
class MaQue implements Bird{
public void fly() {
System.out.println("飞200米...");
}
}
// 增强的类
class DaMaQue implements Bird{
private Bird bird;
public DaMaQue(Bird bird){
this.bird = bird;
}
public void fly() {
bird.fly();
System.out.println("飞500米...");
}
}
测试:
@Test
public void demo1(){
// Bird bird = new MaQue();
// bird.fly();
Bird bird = new DaMaQue(new MaQue());// IO流
bird.fly();
}
***** 装饰者这种增强某个类的方法使用条件的:
* 1.增强类和被增强类实现相同的接口.
* 2.在增强的类中获得到被增强类的引用.
***** 缺点:
* 接口中的方法特别多!
* 动态代理 :
1.2.3 DBCP连接池:
DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。
使用DBCP连接池
1.引入jar包:
* commons-dbcp-1.4.jar
* commons-pool-1.5.6.jar
2.核心类:
* 手动设置参数.
* 采用配置文件的方式.
1.2.4 C3P0连接池:
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等
使用C3P0连接池
1.导入jar包:
* c3p0-0.9.1.2.jar
2.ComboPooledDataSource
1.2.5 Tomcat内置连接池:(JNDI技术.)
JNDI:Java Naming and Directory Interface,Java命名和目录接口.
* 对一个Java对象起一个名字.通过这个名称查找到该对象.
JNDI连接池:
* 需要有一个配置:<Context>标签的配置.
* <Context>标签:在Tomcat学习的时候使用.配置Tomcat的虚拟路径.
* <Context>标签可以配置在三个地方:
* Tomcat/conf/context.xml:可以被Tomcat下的所有的虚拟主机、虚拟路径访问!
* Tomcat/conf/Catalina/localhost/context.xml:可以Tomcat下的localhost虚拟主机下的所有路径访问!
* 当前的工程下/META-INF/context.xml:只能被当前的工程访问!
使用JNDI连接池:
* 1.在工程的/META-INF/创建一个context.xml
<Context>
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource" username="root" password="123"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day18"
maxActive="8" maxIdle="4"/>
</Context>
* 2.Tomcat维护这些连接.这些连接是由Tomcat服务器创建的.
* 必须在Tomcat/lib下copy一个数据库驱动包.
* 3.操作连接池这个类必须是运行在Tomcat服务器中的类!
* 运行在Tomcat中的类是Servlet!!!
jdbc事务处理和连接池的更多相关文章
- eclipse下jdbc数据源与连接池的配置及功能简介
今天在做四则运算网页版的时候遇到了一个困惑,由于需要把每个产生的式子存进 数据库,所以就需要很多次重复的加载驱动,建立连接等操作,这样一方面写程序不方便,加大了程序量,另一方面,还有导致数据库的性能急 ...
- jdbc基础 (五) 连接池与数据源 DBCP以及C3P0的使用
一.连接池的概念和使用 在实际应用开发中,特别是在WEB应用系统中,如果JSP.Servlet或EJB使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接.打开数据库.存取数 ...
- JDBC第一天连接池案例
JDBC,JDBC的工具类JDBC 连接从连接池中拿: 创建连接池的语句: package day01; import java.sql.Connection; import java.sql.Dri ...
- JDBC创建mysql连接池代码
1.底层实现类(DBConnection) package JDBC.JDBCPool.MyJDBCPool; import java.sql.Connection; import java.sql. ...
- 【JDBC】C3P0连接池的使用
C3P0连接池的c3p0-config.xml配置文件 <?xml version="1.0" encoding="UTF-8"?> <c3p ...
- Spring整合JDBC和Druid连接池
我的博客名为黑客之谜,喜欢我的,或者喜欢未来的大神,点一波关注吧!顺便说一下,双十二快到了,祝大家双十二快乐,尽情的买买买~ 如果转载我的文章请标明出处和著名,谢谢配合. 我的博客地址为: https ...
- jdbc事务、连接池概念、c3p0、Driud、JDBC Template、DBUtils
JDBC 事务控制 什么是事务:一个包含多个步骤或者业务操作.如果这个业务或者多个步骤被事务管理,则这多个步骤要么同时成功,要么回滚(多个步骤同时执行失败),这多个步骤是一个整体,不可分割的. 操作: ...
- jdbc pool java连接池技术
1 ConnectPool .java: 2 3 package pool; 4 5 /** 6 * Title: ConnectPool.Java 7 * Description: 连接池治理器 8 ...
- 常用的jdbc的Druid连接池配置
spring: datasource: username: root password: 888888 url: jdbc:mysql://localhost:3306/mybatis driver- ...
随机推荐
- Samus驱动中的Document条件
今天要说一个东西就是Samus驱动里的 Document 和他的一个子类 Op 在Samus驱动的增删改查方法中都有这类的参数传递.. 大致的使用方法是这样.. MongoU.Find<Per ...
- inline-block元素overflow:hidden对齐问题
inline-block元素设置overflow:hidden后,其本身会上移 解决方法:在该元素或其父元素上设置vertical-align:bottom 原因解释:inline-block元素被设 ...
- php函数mt_rand和rand 速度测试
今天在写代码时,看到以前的同时写了一个取随机数,用到了mt_rand(2,19) 就顺手搜了一下,mt_rand和rand的区别. 先看官方的解释 mt_rand 和 rand mt_rand — 生 ...
- CF467D Fedor and Essay 建图DFS
Codeforces Round #267 (Div. 2) CF#267D D - Fedor and Essay D. Fedor and Essay time limit per test ...
- 浙大PAT-1002
1002. 写出这个数 (20) 读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式:每个测试输入包含1个测试用例,即给出自然数n的值.这里保证n小于10100. 输出格式 ...
- Pattern Recognition and Machine Learning (preface translation)
前言 鉴于机器学习产生自计算机科学,模式识别却起源于工程学.然而,这些活动能被看做同一个领域的两个方面,并且他们同时在这过去的十年间经历了本质上的发展.特别是,当图像模型已经作为一个用来描述和应用概率 ...
- Android手机同步电脑端google chrome书签
我先声明:文中FQ 都是博客园自动将中文(fan qiang)转换为FQ的,并不是我本来写的就是FQ~~ 手机和电脑都必须要能登录google(Xee:几乎所有做开发的人都每天的生活都离不开谷歌了,可 ...
- #define 中#和##的作用
#的作用是把后面的参数变成一个字符串. 如,#define f(a) #a f(hello world)相当于"hello world": ##的作用是把两个字符串连接起来. 如, ...
- VBA 实现批量excel文件复制
对于每天要将文件复制到其他多个路径 1 在test文件下新建3个子文件夹 test1,test2,test3 2 在test1下新建li01.xlsx,li02.xlsx,hua01.xlsx,hua ...
- MMTx使用说明
MMTx(MetaMap Transfer)是美国国家医学图书馆建立的用于文本数据挖掘的一种工具. 下面以Medine格式数据为例介绍使用方法 1.在PubMed数据库检索相关的文献. 2.将数据结果 ...