什么是事务?
事务就是一系列必须同步完成的操作,必须所有操作都成功才能能成功,否则一个失败全部失败,事务是一个整体操作
为什么需要事务?
银行案例:
如果在转账的时候发生不可避免的异常(比如断电),此时转账操作已经进行一半,这是数据库中的数据更改了一半,一个人账户余额减少,但另一个账户余额没有增加,会出现金钱纠纷
分割线-----------------------------------------------------
事务模板:
//设置手动提交事务,否则默认自动
conn.setAutoCommit(false);
try{
查询余额
转帐方扣钱
接收方加钱
...
conn.commit();
}catch(Exception e){
//回滚-->释放事务锁
conn.rollBack();
}
/*
* 只有在调用了commit方法之后,数据库才会真正的发生变化.全部成功,调用commit方法.
* 如果失败了一定要调用rollback方法. 原因:
* 在开启事务的时候,就有一个事务锁的存在.必须要调用commit或者rollback才可以释放.
*
* 事务锁锁数据库表,不释放锁的话会造成数据库崩溃
*/
事务的ACID属性:
- 原子性(Atomicity):原子在化学中,是最小单位,不可以再分割了.
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 - 一致性(Consistency):保证数据的完整性.
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏) - 隔离性(Isolation):
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 - 持久性(Durability):
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
事务相关的细节:
1):默认情况下,事务在执行完DML操作就自动提交.
2):查询操作,其实是不需要事务的.但是,一般的,我们在开发中都把查询放入事务中.
3):开发中,代码完全正确,没有异常,但是就是数据库中数据不变.
意识:可能没有提交事务.
获取自动生成的主键
快速注册:
只使用账号密码等信息
此时立马完善资料的话需要拿到刚刚注册时的id
此时就需要获取自动生成id
如何获取:
conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
设置要获取自动生成主键的标记.
ps对象去获取自动生成的主键.getLong(1);
连接池:
为什么要使用连接池(复用)
获取数据库连接对象是一个比较消耗资源,耗时的操作,而我们只是用了一下就断开了,使用连接池可以将连接对象存放在连接池中,起到一个缓存的作用
DBCP/Druid:
DataSource ds=null;
ds=DBCP/DruidDataSourceFactory.createDataSource(ps);
此处约定优于配置
driverClassName
url
username
password
------------------------------------------
public class JdbcUtil {
private JdbcUtil() {
}
private static Properties ps = new Properties();
// 连接池
private static DataSource ds = null;
static {
try {
ps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
// 约定优于配置
ds = DruidDataSourceFactory.createDataSource(ps);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
//使用连接池来获取数据库连接对象
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
throw new RuntimeException("连接异常");
}
}
事务-主键:
public class IEmployeeDAOImplTest {
private IEmployeeDAO dao = new EmployeeDAOImpl();
@Test
public void testTansaction() throws Exception {
ResultSet rs = null;
Connection conn = null;
PreparedStatement ps = null;
try {// 先查询xx账户中是否有足够的余额
String sql = "SELECT * FROM employee WHERE name=? and salary>=?";
conn = JdbcUtil.getConnection();
// 设置事务手动提交,如果不设置则默认为自动提交
conn.setAutoCommit(false);
ps = conn.prepareStatement(sql);
ps.setString(1, "张三");
ps.setBigDecimal(2, new BigDecimal(10000));
rs = ps.executeQuery();
// ps对象使用完后需关闭,否则下方创建新的ps对象会报黄色警告--->ps未关闭
ps.close();
// 如果没有则提示余额不足
if (!rs.next()) {
System.out.println("余额不足");
return;
} else {
// System.out.println(1 / 0);
// 执行扣钱操作,从xx账户中减去 xx元
sql = "UPDATE employee SET salary=salary-? WHERE name=?";
ps = conn.prepareStatement(sql);
ps.setBigDecimal(1, new BigDecimal(100));
ps.setString(2, "张三");
ps.executeUpdate();
ps.close();
// 再给xxx账户中加上xxx元
sql = "UPDATE employee SET salary=salary+? WHERE name=?";
ps = conn.prepareStatement(sql);
ps.setBigDecimal(1, new BigDecimal(100));
ps.setString(2, "李四");
ps.executeUpdate();
// 如果全部成功则提交事务
conn.commit();
}
} catch (Exception e) {
e.printStackTrace();
/*
* 只有在调用了commit方法之后,数据库才会真正的发生变化.全部成功,调用commit方法.
* 如果失败了一定要调用rollback方法. 原因:
* 在开启事务的时候,就有一个事务锁的存在.必须要调用commit或者rollback才可以释放.
*
* 事务锁锁数据库表,不释放锁的话会造成数据库崩溃
*/
// 出现异常则回滚
try {
if (conn != null) {
conn.rollback();
}
} catch (Exception e1) {
e1.printStackTrace();
}
} finally {
JdbcUtil.closeResource(conn, ps, rs);
}
}
@Test
public void testAutoKey() throws Exception {
Connection conn = null;
PreparedStatement ps = null;
String sql = "INSERT INTO employee (name,salary)VALUES(?,?)";
conn = JdbcUtil.getConnection();
// 自动获取主键
ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, "王昭君");
ps.setBigDecimal(2, new BigDecimal(8000));
ps.executeUpdate();
//获取主键
ResultSet gk = ps.getGeneratedKeys();
while(gk.next()){
Object obj=gk.getObject(1);
System.out.println(obj);
}
}