事务的概念和MySQL事务支持:
事务是由一步或者多步数据库操作序列组成的逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。
事务具备4个特性:原子性(Atomicity),一致性(Consistency),隔离性(Isolation)和持续性(Durabilty)。这4个特性也简称为ACID性。
原子性:事务是应用中最小的执行单位
一致性:事务执行的结果,必须是数据库从一个一致性状态,变成另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行中断,某个事务尚未完成而被中断,而该未完成的事务对数据库所做的修改已被写入数据库,此时,数据库就处于一种不正确的状态。比如说银行转账,A向B转账1000元,数据库从A账户减少了1000元,给B账户增加了1000元,如果全部执行成功,数据库处于一致性状态;如果仅仅执行完A账户金额的修改,而没有增加B账户的金额,则数据就处于不一致性状态:因此,一致性时通过原子性来保证的。
隔离性:各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务都时隔离的。也就是说,并发执行的事务之间不能看到对象的中间状态,并发执行的事务之间不能互相影响。
持续性:持续性也成为持久性,指事务一旦提交,对数据所做的任何改变都要记录到永久存储器中,通常就是保存进物理数据库。
数据库的事务由下列语句组成:
一组DML语句,经过这组DML语句修改后的数据将保持较号的一致性。
一条DDL语句
一条DCL语句
DDL语句和DCL语句只有一条,因为DDL和DCL语句都会导致事务立即提交。
当事务所包含的全部数据库操作都成功执行后,应该提交事务(commit),使这些事务修改永久生效。事务的提交一共有两种方式:显示提交与自动提交:
显示提交:使用commit
自动提交:执行DDL或者DCL语句,或者程序正常退出。
当事务所包含的全部数据库操作执行失败后,应该回滚(rollback)事务,是该事务中所做的修改全部失效。事务回滚有两种方式:显示回滚与自动回滚:
显示回滚:使用rollback
自动回滚:数据库操作执行失败,系统错误或者强行退出
MYSQL的事务:
在默认的情况下,MYSQL是关闭事务的,即开启自动提交,在默认的情况下,当用户在MYSQL控制台输入一条DML语句时,这叫DML语句将会立即保存到数据库里(持续性)。如果要开启MYSQL的事务支持,可以显示调用如下命令:
set autocommit=0 #开启事务 set autocommit=1 #自动提交
自动提交和开启事务恰好相反,如果开启自动提交,就是关闭事务;如果开启事务就是关闭自动提交
一旦在MYSQL的命令行窗口中输入了set autocommit=0 开启了事务,该命令行窗口里的所有DML语句都不会立即生效,上一个事务结束后第一条DML语句将开始一个新的事务,而后续执行的所有SQL语句都在该事务中,除非显式使用commit来提交事务,或者正常退出,或者运行DDL,DCL语句导致事务隐式提交。当然也可以使用rollback来回滚结束事务,使用rollback结束事务将导致本次事务中的DML语句所做的修改全部失效。
PS:这里要注意一点的是,在命令行窗口的开启事务是对该命令行窗口有效,其他窗口还是处于mysql的默认状态,即自动提交。
除此之外,若不想关闭整个命令行窗口的自动提交(即关闭事务),而只是想临时性地开始事务,则可以使用MySQL提供的start transaction或begin两个命令,它们都表示临时性地开始一次事务,处于start transaction或者begin后的DML语句不会立即生效,除非使用commit显示提交事务,或者执行DDL,DCL语句的隐式提交事务才会立即生,如下面的SQL语句:
#临时开始事务 begin: #向student_table表中插入3条记录 insert into tb_test values(null,‘张三‘,‘1‘); insert into tb_test values(null,‘李四‘,‘2‘); insert into tb_test values(null,‘赵五‘,‘3‘); #查询表中的记录 (1) select * from tb_test; #回滚事务 rollback; #查询表中的记录 (2) select * from tb_test;
执行上面SQL语句中的第一个查询语句将会看到刚刚插入的3条记录,其他窗口进行查询是看不到该3条记录的,因为该3条记录未提交,也就是还没有写入数据库中。当回滚事务后,执行上面SQL语句的第二个查询语句,该窗口也和其他窗口一样,也看不到那3条记录,因为回滚事务后,对事务中的所有DML语句全都失效,因此该窗口再也查询不出那三条记录。
MYSQL还提供了savepoint来设置事务的中间点,通过使用设置savepoint设置事务的中间点可以让事务回滚到指定中间点,而不是回滚全部事务。如下SQL语句:
savepoint a #设置中间点 rollback to a; 使用rollback回滚到指定中间点
JDBC的事务支持:
JDBC连接也提供了事务支持,JDBC连接的事务支持由Connection提供,Connection默认是自动提交,即关闭事务(一次性只能执行一条DML语句),在这种情况下,每条SQL语句一旦执行,便会立即提交到数据库,永久生效,无法对其进行回滚操作。
可以使用Connection 的setAutoCommit(false)方法来关闭自动提交,开启事务。
可以使用Connection 的setAutoCommit(true)方法来关闭事务,开启自动提交,这也是Connection的默认状态。
一旦开启了事务之后,程序可以向平常一样创建Statement对象,创建了Statement对象之后,可以执行任意多条DML语句,尽管程序都执行了多条DML语句,但这些DML语句所做的修改都不会生效,因为没有提交事务。因此当编写完DML语句后要加上Connection.commit()方法来提交事务,让DML语句生效。如多条DML语句中执行失败,则应该用Connection的rollback()方法来回滚事务。实际上,当Connection遇到一个未处理的SQLExcepition异常时,系统将会非正常退出,即事务隐式回滚;若程序捕获了改异常,则需要在异常处理块中显式地回滚事务。
下面程序示范了JDBC的事务支持:
public class JDBC { public void insertInTransaction(String[] sqls){ try{ conn.setAutoCommit(false); Statement statement = conn.createStatement(); for(String sql : sqls){ statement.execute(sql); System.out.println("SQL语句执行正常"); } conn.commit(); //提交事务 }catch(Exception e){ if(conn != null){ try { conn.rollback(); System.out.println("事务回滚"); } catch (SQLException e1) {e1.printStackTrace();} } } } Connection conn = null; Statement statement = null; public JDBC(String paramFile){ try{ Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("Url"); user = props.getProperty("user"); password = props.getProperty("password"); conn =DriverManager.getConnection(url,user,password); statement =conn.createStatement(); }catch(Exception e){} } public static void main(String[] args) throws SQLException { String[] sqls = new String[]{ "insert into tb_test values(null,‘AA‘,1)", "insert into tb_test values(null,‘BB‘,2)", "insert into tb_test values(null,‘CC‘,3)", "insert into tb_test values(null,‘00‘,‘00‘)", //错误的,因为第三个字段的数据类型为int,不能传入字符串进去 "insert into tb_test values(null,‘DD‘,4‘)", }; jdbc_2.insertInTransaction(sqls); } }
上述代码中,一共提交了5条DML语句,其中第四条是错误的,但第四条DML语句执行时,因为是错误的,会出现SQLException异常,程序将这个异常捕获,并执行了事务回滚,去查看对应数据库表,可以发现前三条DML语句在程序中尽管执行了,但还未写入到数据库中。保证了数据库的一致性。运行效果如下:
SQL语句执行正常
SQL语句执行正常
SQL语句执行正常
SQL语句执行正常
事务回滚
使用连接池管理连接:
数据库连接的建立以及关闭都是极其耗费系统资源的操作,在多层结构的应用环境中,这种资源的耗费对系统性能影响尤其明显。当获得的数据库连接,一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完后立即关闭连接。频繁地打开,关闭连接造成系统性能低下。
为了解决这个问题,Java提供了数据库连接池,当应用程序启动时,系统会主动建立足够的数据库连接,并将这些连接组成一个连接池。每次应用程序请求数据库连接时,无需重新打开连接,而是从连接池中取出已有的连接使用,当应用程序使用完毕后,该连接不用完毕,而是直接将连接归回连接池。通过使用连接池,将大大提高程序的运行效率。
数据库连接池是Connection对象的工厂,数据库连接池的常用参数如下:
1. 数据库的初始连接数
2. 连接池的最大连接数。
3. 连接池的最小连接数
4. 连接池每次增加的容量
JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由商用服务器(如WebLogic,WebSphere)等提供实现,也有一些开源组织提供实现(如DBCP和C3P0)。这里推荐大家都使用C3P0。
DBCP数据源:
DBCP数据源是由Apache软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:common-pool。如果要使用DBCP连接池,需要将如下的两个jar文件添加到系统或者开发软件中:
commons-dbcp.jar:连接池的实现
commons-pool.jar:连接池实现的依赖库
Tomcat的连接池正是采用该连接池实现的。数据库连接池既可以与应用服务器整合使用,也可以由应用程序独立使用。下面的代码示范了使用DBCP来获取数据库连接方式:
BasicDataSource ds = new BasicDataSource(); //创建数据源对象 ds.setDriverClassName("com.mysql.jdbc.Driver"); //设置连接池所需的驱动 ds.setUrl("jdbc://mysql://localhost:8080/test"); //设置连接数据库的URL ds.setUsername("root"); //设置连接数据库的用户名 ds.setPassword("root"); //设置连接数据库的密码 ds.setInitialSize(5); //设置连接池的初始连接数 ds.setMaxActive(20); //设置连接池最多可有20个活动连接数 ds.setMinIdle(2); //设置连接池最少由2个空闲的连接
数据源与数据库连接不同,数据源无须创建多个,它是产生数据库连接的工厂,因此整个应用只需有一个数据源便可,建议是将数据源设置成类成员变量,并且在应用开始时立即初始化数据源对象。
Connection conn = ds.getConnection(); //从数据库连接池中获取连接 conn.close() //关闭连接,并将该连接还给数据库连接池
C3P0数据源:
相比之下,C3P0数据源性能更胜一筹,Hibernate就推荐使用该连接池。C3P0连接池不仅可以自动清理不在使用的Connection,还可以自动清理Statement和ResultSet。若要使用C3P0,需要将c3p0-0.9.1.2.jar (有不同的版本)的jar文件添加到系统或者开发软件中。
下面代码通过c3p0连接池获得数据库连接:
ComboPooledDataSource ds = new ComboPooledDataSource(); //创建连接池实例 ds.setDriverClass("com.mysql.jdbc.Driver"); //设置连接池连接数据库所需的驱动 ds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test"); //设置连接数据库的URL ds.setUser("root"); //设置连接数据库的用户名 ds.setPassword("root"); //设置连接数据库的密码 ds.setMaxPoolSize(40); //设置连接池的最大连接数 ds.setMinPoolSize(10); //设置连接池的最小连接数 ds.setInitialPoolSize(10); //设置连接池的初始连接数 ds.setMaxStatements(180); //设置连接池的缓存Statement的最大数
C3P0获取数据库的连接操作代码与DBCP源获取数据库的连接是一样的;关闭连接的操作也是一样的:
Connection conn = ds.getConnection(); //从数据库连接池中获取连接 conn.close() //关闭连接,并将该连接还给数据库连接池
原文:https://www.cnblogs.com/hjlin/p/11441224.html