原子性
一致性
隔离性
持久性
如果JDBC连接是在自动提交模式下,它在默认情况下,那么每个SQL语句都是在其完成时提交到数据库。
这可能是对简单的应用程序,但有三个原因,你可能想关闭自动提交和管理自己的事务:
-
为了提高性能
-
为了保持业务流程的完整性
-
使用分布式事务
若要控制事务,以及何时更改应用到数据库。它把单个SQL语句或一组SQL语句作为一个逻辑单元,而且如果任何语句失败,整个事务失败。
若要启用,而不是JDBC驱动程序默认使用auto-commit模式手动事务支持,使用Connection对象的的setAutoCommit()方法。如果传递一个布尔值false到setAutoCommit(),关闭自动提交。可以传递一个布尔值true将其重新打开。
例如,如果有一个名为conn Connection对象,以下代码来关闭自动提交:
conn.setAutoCommit(false);
提交和回滚
一旦已经完成了变化,要提交更改,然后调用commit(在连接对象)方法,如下所示:
conn.commit( );
否则回滚更新对数据库所做的使用命名连接conn,使用下面的代码:
conn.rollback( );
下面的例子演示了如何使用一个提交和回滚对象:
try{
//Assume a valid connection object conn
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
//Submit a malformed SQL statement that breaks
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Singh')";
stmt.executeUpdate(SQL);
// If there is no error.
conn.commit();
}catch(SQLException se){
// If there is any error.
conn.rollback();
}
在这种情况下没有上述INSERT语句会成功,一切都将被回滚。
为了更好地理解,建议学习事务提交实例代码.
使用保存点:
新的JDBC3.0保存点的接口提供了额外的事务控制。他们的环境中,如Oracle的PL/ SQL中的大多数现代的DBMS支持保存点。
当设置一个保存点在事务中定义一个逻辑回滚点。如果发生错误,过去一个保存点,则可以使用rollback方法来撤消要么所有的改变或仅保存点之后所做的更改。
Connection对象有两个新的方法,可帮助管理保存点:
-
setSavepoint(String savepointName): 定义了一个新的保存点。它也返回一个Savepoint 对象。
-
releaseSavepoint(Savepoint savepointName): 删除一个保存点。请注意,它需要一个Savepoint 对象作为参数。这个对象通常是由setSavepoint()方法生成一个保存点。
有一个rollback ( String savepointName ) 方法回滚工作到指定的保存点。
下面的例子演示如何使用Savepoint对象:
try{
//Assume a valid connection object conn
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
//set a Savepoint
Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
String SQL = "INSERT INTO Employees " +
"VALUES (106, 20, 'Rita', 'Tez')";
stmt.executeUpdate(SQL);
//Submit a malformed SQL statement that breaks
String SQL = "INSERTED IN Employees " +
"VALUES (107, 22, 'Sita', 'Tez')";
stmt.executeUpdate(SQL);
// If there is no error, commit the changes.
conn.commit();
}catch(SQLException se){
// If there is any error.
conn.rollback(savepoint1);
}
在这种情况下没有上述INSERT语句会成功,一切都将被回滚。
package com.driver; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import org.junit.Test; /** * 事务:一组操作数据的逻辑单元 * * 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。 事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。 二.事务的 ACID 事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。 1 、原子性 事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做 2 、一致性 事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。 3 、隔离性 一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 4 、持续性 也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。 * * * * * * 脏读 :对于两个事务T1,T2,T1读取了被T2更新但没有提交的字段,若T2回滚,T1读取到的就是无效的 * 不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段之后T1再次读取同一个字段值就不同了 * 幻读:对于两个事务T1,T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行之后,如果T1再次读取同一个表就会多出几行 * * 事务的隔离级别越高,并发性越差 * 数据库的隔离级别: * 读未提交数据,会出现脏读,不可重复读,幻读 * * 读已提交数据 :只允许已提交的 会出现 不可重复读和幻读 * * 可重复读:在当前事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,幻读还会存在 * * * 串行化:在当前事务持续期间,禁止其他事务对该表执行插入,更新,和删除操作,所有并发问题都可以避免,单性能十分低下 * * oracle 支持2种事务隔离级别 读已提交数据,串行化 默认为 读已提交数据 * * mysql支持4种事务隔离级别,默认为 可重复读 查看当前的默认事务 SELECT @@tx_isolation * * DataSource通常被称为数据源,包括连接池和连接池管理 * 数据库连接池:数据库连接池的基本思想就是为数据库建立一个缓冲池,预先在缓冲池中放入一定数据的链接 * * 当需要建立连接数据库连接时,只需从缓冲池中取出一个,使用完毕之后再放回去 * * 数据库连接池负责分配,管理和释放数据库连接池, 它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个 * * * * @author Administrator * */ public class DriverTest { public static String s; static{ s="1222222222"; System.out.println("静态代码块"); } public DriverTest() { System.out.println("构造方法"); } @Test public void test() throws SQLException{ Driver driver = new com.mysql.jdbc.Driver(); String url = "jdbc:mysql://127.0.0.1:3306/test"; Properties properties = new Properties(); properties.put("user", "root"); properties.put("password", "root"); Connection connection = driver.connect(url, properties); System.out.println(connection); } /** * 通用的 * @return * @throws IOException * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public void getConnection() throws Exception{ InputStream inputStream = DriverTest.class.getClass().getClassLoader().getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); properties.load(inputStream); Driver driver = (Driver) Class.forName(properties.getProperty("driver")).newInstance(); Properties info = new Properties(); info.put("user", properties.getProperty("user")); info.put("password", properties.getProperty("password")); Connection connection = driver.connect(properties.getProperty("jdbcUrl"), info); System.out.println(connection); //return connection; } /** * DriverManager * @return * @throws Exception */ public Connection getConnection2() throws Exception{ InputStream inputStream = Thread.currentThread().getClass().getClassLoader().getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); properties.load(inputStream); String url = properties.getProperty("jdbcUrl"); String user = properties.getProperty("user"); String password = properties.getProperty("password"); String driverClass = properties.getProperty("driver"); //加载数据库驱动程序 //会调用 mysql 驱动 Driver 里的静态代码块 Class.forName(driverClass); Connection connection = DriverManager.getConnection(url, user, password); //System.out.println(connection); //DriverManager可以注册多个驱动程序 链接多个数据库 return connection; } @Test public void testStatement() throws Exception{ Connection connection = getConnection2(); //是否自动提交 connection.setAutoCommit(false); //设置事务的隔离级别 //connection.setTransactionIsolation(level); String sql = "insert into people (id,name1,dep_id) values (22,'张三',1)"; Statement statement = connection.createStatement(); int a = statement.executeUpdate(sql); connection.commit(); System.out.println(a); statement.close(); connection.close(); } public void update(String sql){ Connection connection = null; Statement statement = null; try{ connection = getConnection2(); statement = connection.createStatement(); statement.executeUpdate(sql); }catch(Exception e){ e.printStackTrace(); }finally{ close(statement, connection); } } /** * PreparedStatement 是Statement的子接口 * 1会将sql 先编译好 预编译放到缓存中,提升性能,和sql的执行效率 * 2防止sql注入 * @param args */ public void testPreparedStatement(String sql,Object...args){ Connection connection = null; PreparedStatement preparedStatement = null; try { connection = getConnection2(); preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i+1, args[i]); } preparedStatement.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally{ close(preparedStatement, connection); } } @Test public void saveOrUpdate(){ Connection connection = null; PreparedStatement preparedStatement = null; try { connection = getConnection2(); //pstmt = conn.prepareStatement("INSERT INTO staff(name, age, sex,address, depart, worklen,wage) VALUES (?, ?, ?, ?, ?, ?, ?)"); // String sql= "INSERT INTO USER(id,NAME) VALUES( ?, ?)"; //String sql = "insert into user(id,name,age,birthday,password) values (?,?,?,?,?)"; //String sql = "insert into user(id,name) values(?,?)"; preparedStatement = connection.prepareStatement("INSERT INTO USER(id,NAME) VALUES( ?, ?)"); //preparedStatement.setObject(1, 1); //preparedStatement.setObject(2, "张三"); preparedStatement.setInt(1, 1); preparedStatement.setString(2, null); preparedStatement.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally{ //close(preparedStatement, connection); } } public void testResultSet(){ Connection connection = null; Statement statement = null; ResultSet resultSet = null; try{ connection = getConnection2(); statement = connection.createStatement(); String sql = "select * from department"; resultSet = statement.executeQuery(sql);
ResultSetMetaData md = resultSet.getMetaData();
int columnCount = md.getColumnCount();
List tempList = new ArrayList(); //指针下移到第一行数据 while(resultSet.next()){ System.out.println(resultSet.getInt(1)); System.out.println(resultSet.getString("department_name"));
Map rowData = new HashMap();
// {SRID=2c908f516abf6030016abf62c3f20010, FAWENDANWEI=null, FAWENRIQI=2019-05-16 14:44:42, ISREAD=0, RN=4, WENHAO=null, DID=2c908f516abf6054016abf63c157003c, TUIWEN=null, TS=2019-05-16, ISQIANSHOU=0, TITLE=测试8}
for (int i = 1; i <= columnCount; i++) {
rowData.put(md.getColumnName(i), rs.getObject(i));
}
tempList.add(rowData);
} }catch(Exception e){ }finally{ if(resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } close(statement, connection); } } public void close(Statement statement,Connection connection){ if(statement!=null){ try { statement.close(); } catch (SQLException e) { } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { } } } public static void main(String[] args) throws Exception { //Class.forName("com.driver.DriverTest"); String sql = "insert into user(id,name,age,birthday,password) values (?,?,?,?,?)"; DriverTest test = new DriverTest(); test.testPreparedStatement(sql, 1,"张三"); } }
mysql的事务隔离级别
https://segmentfault.com/a/1190000016566788