mysql jdbc 单利_jdbc 编程(二)及 事务

JDBC处理大文本,JDBC处理图片,JDBC进行批处理    数据库连接池

1. 事务(Transaction,简写为tx):

所谓事务是用户定义的一组操作,使数据从一种状态变换到另一种状态,要么同时成功,要么同时失败。

2. 事务的操作:

先定义开始一个事务,然后对数据作修改操作,这时如果提交(commit),这些修改就永久地保存下来,如果回退(rollback),数据库管理系统将放弃您所作的所有修改而回到开始事务时的状态。

3. 事务的ACID属性:

Atomic原子性、Consistency一致性、Isolation   [ˌaɪsəˈleɪʃn] 隔离性和 Durability 持久性。

1、原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

2、一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

3、隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

4、持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。)

4. 并发下事务会产生的问题

1、脏读

所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务-->取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读。

2、不可重复读

所谓不可重复读,就是指在一个事务里面读取了两次某个数据,读出来的数据不一致。还是以银行取钱为例,事务A开启事务-->查出银行卡余额为1000元,此时切换到事务B事务B开启事务-->事务B取走100元-->提交,数据库里面余额变为900元,此时切换回事务A,事务A再查一次查出账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余额数据不一致,这就是不可重复读。

3、幻读

所谓幻读,就是指在一个事务里面的操作中发现了未被操作的数据。比如学生信息,事务A开启事务-->修改所有学生当天签到状况为false,此时切换到事务B,事务B开启事务-->事务B插入了一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样。幻读出现的前提是并发的事务中有事务发生了插入、删除操作。

5. MySQL事务隔离级别

① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

② Repeatable read (可重复读):可避免脏读、不可重复读的发生。

③ Read committed (读已提交):可避免脏读的发生。

④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。

在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。

1. 在MySQL数据库中查看当前事务的隔离级别:select @@tx_isolation;

2.在MySQL数据库中设置事务的隔离 级别:  记住:设置数据库的隔离级别一定要是在开启事务之前!

set [glogal | session] transaction isolation level 隔离级别名称;

set tx_isolation=’隔离级别名称;

注意:   session 表示设置当前会话事务隔离级别,

比如MyBatis,getSqlSession()的时候,只针对这一次拿到的Session有效;比如CMD命令行,只对这一次的窗口有效。

glogal表示设置全局事务隔离级别:针对此后所有的会话有效,当前已经存在的会话不受影响。

3. 如果是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)方法之前。调用Connection对象的setTransactionIsolation(level)即可设置当前链接的隔离级别,至于参数level,可以使用Connection对象的字段:

3bea775569076c650f20bde77aedce14.png

在JDBC中设置隔离级别的部分代码:

b7678e375ffb6a60c3d137f8e1250b32.png

后记:隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。

6. 如何在代码中去处理事务:

1.在JDBC中,事务是默认提交的.  必须先设置事务为手动提交.

connection对象.setAutoCommit(false);//设置事务为手动提交.

2.手动的提交事务.

connection对象.commit();

3.若出现异常必须回滚事务:

不回滚事务,总余额依然是正确的. 若不回滚事务,不会释放数据库资源.

connection对象.rollback();

7. 事务示例:

银行转帐功能: bank / money

过儿和姑姑:

过儿 : 10000块钱

姑姑 : 0块钱

转账:过儿要给姑姑转1000块钱

分析:转钱需要提供两条sql,但是程序员也会出错,比较代码写错了.

①update bank set money = money +1000 where name = '姑姑'

②代码出错,     会导致过儿账户钱少了,而姑姑账户没有收到钱,钱去哪了?

③update bank set money =  money -1000 where name= '过儿'

解决办法 代码:

数据库:

feb7825a1f065b895ad1ffa28c8f3634.png

CREATE TABLE `bank` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(255) DEFAULT NULL,

`money` double DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

com.domain包下的  实体类 Bank

package com.domain;

public class Bank {

private String name;

private double money;

public Bank(String name, double money) {

super();

this.name = name;

this.money = money;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public double getMoney() {

return money;

}

public void setMoney(double money) {

this.money = money;

}

public Bank() {

// TODO Auto-generated constructor stub

}

@Override

public String toString() {

return "Bank [name=" + name + ", money=" + money + "]";

}

}

com.dao 包下的 IBankDao 接口

package com.dao;

import com.domain.Bank;

public interface IBankDao {

void transMoney(Bank b1,Bank b2, double money);

}

com.dao .impl 包 的  BankDaoImpl 实现类

package com.dao.impl;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import com.Util.JDBCUtil;

import com.dao.IBankDao;

import com.domain.Bank;

public class BankDaoImpl implements IBankDao {

// 单利模式获取 BankDaoImpl 类对象

private BankDaoImpl (){}

private static BankDaoImpl instance = null;

public static BankDaoImpl getInstance(){

if(instance == null){

return instance = new BankDaoImpl();

}

return null;

}

@Override

public void transMoney(Bank b1, Bank b2, double money) {

Connection conn = null;

PreparedStatement ps = null;

try {

conn = JDBCUtil.getInstance().getConnection();

//在JDBC中,事务是默认提交的true. 必须先设置事务为手动提交false

conn.setAutoCommit(false);

ps = conn.prepareStatement("update bank set money = money - ? where name = ? ");

ps.setDouble(1, money);

ps.setString(2, b1.getName() );

ps.executeUpdate();

//int a = 1/0; //错误代码

ps = conn.prepareStatement("update bank set money = money + ? where name = ?");

ps.setDouble(1, money);

ps.setString(2, b2.getName());

ps.executeUpdate();

conn.commit(); //手动进行提交

} catch (Exception e) {

try {

conn.rollback(); //数据回滚

} catch (SQLException e1) {

e1.printStackTrace();

}

}

JDBCUtil.getInstance().closeAll(null,ps,conn);

}

}

测试类

public class TestBank {

@Test

public void trans(){

Bank b1 = new Bank("过儿",10000);

Bank b2 = new Bank("姑姑",0);

BankDaoImpl.getInstance().transMoney(b1, b2, 1000);

}

}

8. 如何在JDBC中保存数据的时候获取自动生成的主键呢?

Statement方式:

int executeUpdate(String sql,  int autoGeneratedKeys):执行SQL:

参数:autoGeneratedKeys,是否需要返回自动生成的主键.常量值:Statement.RETURN_GENERATED_KEYS.

ResultSet getGeneratedKeys():获取自动生成的主键

示列代码如下:

@Testpublic void statementTest() throwsException {

String sql= "insert into student (name, age) values ('zhangsan', 30)";

Connection conn=JdbcUtil.getConn();

Statement st=conn.createStatement();//statement.executeUpdate(sql);

st.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);

ResultSet rs= st.getGeneratedKeys();

if (rs.next()) {

Long id = rs.getLong(1);

System.out.println(id);

}

JdbcUtil.close(conn, st,null);

}

PreparedStatement方式:

PreparedStatement prepareStatement(String sql,int autoGeneratedKeys)  :

创建PreparedStatement对象,病指定是否需要返回生成的主键. 常量值:Statement.RETURN_GENERATED_KEYS

示列代码:

public classPreparedStatementTest {

@Testpublic void preparedStatement() throwsException {

String sql= "insert into student (name,age) values (?,?)";

Connection conn=JdbcUtil.getConn();

PreparedStatement ps=conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

ps.setString(1, "张三");

ps.setInt(2, 24);

ps.executeUpdate();

ResultSet rs= ps.getGeneratedKeys();

if (rs.next()) {

Long id = rs.getLong(1);

System.out.println(id);

}

JdbcUtil.close(conn, ps,null);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值