jdbcTemplate類的入門
方式一
POM.XML
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-aop</groupId> <artifactId>spring-aop</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- spring ioc組件須要的依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.1.RELEASE</version> </dependency> <!-- 基於AspectJ的aop依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- spring 事務管理和JDBC依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.1.RELEASE</version> </dependency> <!-- spring 單元測試組件包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.1.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- MySql數據庫驅動包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency> </dependencies> </project>
修改jdk版本html
<build> <plugins> <!-- 配置Maven的JDK編譯級別 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>
測試單元
package com.cyb.spring.test; import org.junit.Test; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; public class TestJdbcTemplate { @Test public void test() { try { //建立鏈接池,先使用spring框架內置的鏈接池 DriverManagerDataSource dataSource =new DriverManagerDataSource(); //數據庫驅動程序 //dataSource.setDriverClassName("com.mysql.jdbc.Driver"); //舊驅動程序 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //新驅動程序 //數據庫鏈接字符串 dataSource.setUrl("jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"); //帳號 dataSource.setUsername("root"); //密碼 dataSource.setPassword("root"); //建立模板類 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //完成數據的添加 int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"測試人員"); } catch (Exception e) { e.printStackTrace(); } } }
方式二
spring.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 管理DataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!-- set方法注入屬性,和類中的成員屬性無關,和set方法名稱有關,好比有一個屬性叫username,可是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> </beans>
單元測試
package com.cyb.spring.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"測試人員"); } catch (Exception e) { e.printStackTrace(); } } }
Spring 管理第三方DataSource
經常使用數據源鏈接池
- dbcp
- c3p0
- druid(阿里出品)
管理DBCP鏈接池
maven工程加入依賴java
pom.xmlmysql
<dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>
spring.xmlspring
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 管理第三方DataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- set方法注入屬性,和類中的成員屬性無關,和set方法名稱有關,好比有一個屬性叫username,可是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> </beans>
單元測試sql
package com.cyb.spring.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"測試人員22"); } catch (Exception e) { e.printStackTrace(); } } @Test public void test2() { try { //建立鏈接池,先使用spring框架內置的鏈接池 DriverManagerDataSource dataSource =new DriverManagerDataSource(); //數據庫驅動程序 //dataSource.setDriverClassName("com.mysql.jdbc.Driver"); //舊驅動程序 dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); //新驅動程序 //數據庫鏈接字符串 dataSource.setUrl("jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"); //帳號 dataSource.setUsername("root"); //密碼 dataSource.setPassword("root"); //建立模板類 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); //完成數據的添加 int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"測試人員"); } catch (Exception e) { e.printStackTrace(); } } }
使用JdbcTemplate完成增刪改查操做
s_user.java數據庫
package com.cyb.spring.test; public class s_user { private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "s_user [id=" + id + ", name=" + name + ", age=" + age + "]"; } private String name; private int age; }
TestjdbcTemplate.javaexpress
查詢
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { //第一個參數:執行的sql語句 //第二個參數:結果映射處理器(RowMapper) //第三個參數:sql語句中的入參 List<s_user> queryList = jdbcTemplate.query("select * from s_user",new MyBeanMapper(),null); System.out.println(queryList); } } class MyBeanMapper implements RowMapper { public s_user mapRow(ResultSet rs, int rowNum) throws SQLException { s_user user=new s_user(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setAge(rs.getInt("age")); return user; } }
插入
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("insert into s_user values (null,?,?)",22,"測試人員22"); } catch (Exception e) { e.printStackTrace(); } } }
更新
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("update s_user set name=?,age=? where id=?","測試人員",19,5); } catch (Exception e) { e.printStackTrace(); } } }
刪除
package com.cyb.spring.test; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class TestJdbcTemplate { @Autowired private JdbcTemplate jdbcTemplate; @Test public void test() { try { int res = jdbcTemplate.update("delete from s_user where id=?",5); } catch (Exception e) { e.printStackTrace(); } } }
Spring DAO 開發之JdbcDaoSupport
案例設計
- 編寫轉帳案例(包括業務層和持久層)
- 編寫DAO時引入JdbcDaoSupport的使用
實現
項目結構圖apache
數據庫表字段編程
pom.xmlapi
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>spring-aop</groupId> <artifactId>spring-aop</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- spring ioc組件須要的依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.2.1.RELEASE</version> </dependency> <!-- 基於AspectJ的aop依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <!-- spring 事務管理和JDBC依賴包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.1.RELEASE</version> </dependency> <!-- spring 單元測試組件包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.1.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- MySql數據庫驅動包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency> <!-- dbcp鏈接池依賴包 --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.1</version> </dependency> </dependencies> <build> <plugins> <!-- 配置Maven的JDK編譯級別 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
AccountDao.java
package com.cyb.spring.dao; public interface AccountDao { void update(String name, int money); int queryMoney(String name); }
AccountDaoImpl.java
方式一
package com.cyb.spring.dao; import java.sql.ResultSet; import java.sql.SQLException; import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; @Repository public class AccountDaoImpl implements AccountDao { @Resource private JdbcTemplate jdbcTemplate; public void update(String name, int money) { // jdbc操做 // MyBatis操做 // JdbcTemplate操做 jdbcTemplate.update("UPDATE s_account set money=" + money + " where name='" + name + "'"); } public int queryMoney(String name) { int money = jdbcTemplate.queryForObject("select money from s_account where name=?", new IntMapper(), name); return money; } } class IntMapper implements RowMapper<Integer> { public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getInt("money"); } }
方式二
package com.cyb.spring.dao; import java.sql.ResultSet; import java.sql.SQLException; import javax.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.stereotype.Repository; @Repository public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { public void update(String name, int money) { this.getJdbcTemplate().update("UPDATE s_account set money=" + money + " where name='" + name + "'"); } public int queryMoney(String name) { int money=this.getJdbcTemplate().queryForObject("select money from s_account where name=?", new IntMapper(), name); return money; } } class IntMapper implements RowMapper<Integer> { public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getInt("money"); } }
AccountService.java
package com.cyb.spring.service; public interface AccountService { void transfer(String from, String to, int money); }
AccountServiceImpl.java
方式一
package com.cyb.spring.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.cyb.spring.dao.AccountDao; @Service public class AccountServiceImpl implements AccountService { @Resource private AccountDao dao; public void transfer(String from, String to, int money) { //先查詢from帳戶的錢 int fromMoney=dao.queryMoney(from); // 對from帳戶進行扣錢操做 dao.update(from, fromMoney-money); // 對to帳戶加錢操做 int toMoney=dao.queryMoney(to); dao.update(to, toMoney+money); } }
方式二
package com.cyb.spring.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import com.cyb.spring.dao.AccountDao; @Service public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public AccountDao getAccountDao() { return accountDao; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void transfer(String from, String to, int money) { //先查詢from帳戶的錢 int fromMoney=accountDao.queryMoney(from); // 對from帳戶進行扣錢操做 accountDao.update(from, fromMoney-money); // 對to帳戶加錢操做 int toMoney=accountDao.queryMoney(to); accountDao.update(to, toMoney+money); } }
spring-transfer.xml
方式一
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 從底層往上層配 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- set方法注入屬性,和類中的成員屬性無關,和set方法名稱有關,好比有一個屬性叫username,可是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <!-- AccountDao和AccountService --> <context:component-scan base-package="com.cyb.spring"></context:component-scan> </beans>
方式二
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 從底層往上層配 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- set方法注入屬性,和類中的成員屬性無關,和set方法名稱有關,好比有一個屬性叫username,可是set方法:setName --> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 管理jdbcTemplate --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg name="dataSource" ref="dataSource"></constructor-arg> </bean> <!-- AccountDao和AccountService --> <bean id="accountService" class="com.cyb.spring.service.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <bean id="accountDao" class="com.cyb.spring.dao.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
AccountServiceTest.java
package com.cyb.spring.service; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring-transfer.xml") public class AccountServiceTest { @Autowired private AccountService Service; @Test public void testTransfer() { Service.transfer("老公", "老婆", 500); } }
Spring 應用之事務
事務介紹
- 事務:指的是邏輯上一組操做,組成這個事務的各個執行單元,要麼一塊兒成功,要麼一塊兒失敗!
- 事務的特性(ACID)
- 原子性(Atomicty)
- 原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾。
- 一致性(Consistency)
- 一致性是指事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態。
- 拿轉帳來講,假設用戶A和用戶B二者的錢加起來一共是5000,那麼無論A和B之間如何轉帳,賺屢次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
- 隔離性(Isolation)
- 隔離性是當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。
- 持久性(Durability)
- 持久性是指一個事務一旦被提交了,那麼對數據庫種的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。
- 原子性(Atomicty)
事務併發問題(隔離性致使)
在事務的併發操做中可能會出現一些問題
- 髒讀:一個事務讀取到另外一個事務未提交的數據
- 不可重複讀:一個事務因讀取到另外一個事務已提交的數據。致使對同一條記錄讀取兩次以上的結果不一致。update操做
- 幻讀:一個事務因讀取到另外一個事務已提交的數據。致使對同一張表讀取兩次以上結果不一致。insert、delete操做
事務隔離級別
爲了不上面出現的幾種狀況,在標準SQL規範中,定義了4個事務隔離級別,不一樣的隔離級別對事務的處理不一樣。
四種隔離級別(由低到高):
- Read uncommitted(讀未提交):最低級別,任何狀況都沒法保證。
- Read committed(讀已提交):可避免髒讀的發生
- Repeatable read(可重複讀):可避免髒讀、不可重複讀的發生
- Serializable(串行化):可避免髒讀、不可重複讀、幻讀的發生
默認隔離級別
大多數數據庫的默認隔離級別是Read committed,好比Oracle、DB2等。
MySQL數據庫的默認隔離級別是Repeatable read。
注意事項
級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。
對於多數應用程序,能夠優先考慮把數據庫系統的隔離級別設爲Read Committed。它可以避免髒讀取,並且具備較好的併發性能。儘管它會致使不可重複讀、幻讀這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用悲觀鎖或樂觀鎖來控制。
Spring 框架的事務管理相關的類和API
Spring 並不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委託給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。Spring事務管理器的接口是PlatformTransactionManager,經過這個接口,Spring爲各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,可是具體的實現就是各個平臺本身的事兒了。
1. PlatformTransactionManager接口 -- 平臺事務管理器.(真正管理事務的類)。該接口有具體的實現類,根據不一樣的持久層框架,須要選擇不一樣的實現類!
2. TransactionDefinition接口 -- 事務定義信息.(事務的隔離級別,傳播行爲,超時,只讀)
3. TransactionStatus接口 -- 事務的狀態(是否新事務、是否已提交、是否有保存點、是否回滾)
4. 總結:上述對象之間的關係:平臺事務管理器真正管理事務對象.根據事務定義的信息TransactionDefinition 進行事務管理,在管理事務中產生一些狀態.將狀態記錄到TransactionStatus中
5. PlatformTransactionManager接口中實現類和經常使用的方法
1. 接口的實現類
* 若是使用的Spring的JDBC模板或者MyBatis(IBatis)框架,須要選擇DataSourceTransactionManager實現類
* 若是使用的是Hibernate的框架,須要選擇HibernateTransactionManager實現類
2. 該接口的經常使用方法
* void commit(TransactionStatus status)
* TransactionStatus getTransaction(TransactionDefinition definition)
* void rollback(TransactionStatus status)
6. TransactionDefinition
1. 事務隔離級別的常量
* static int ISOLATION_DEFAULT -- 採用數據庫的默認隔離級別
* static int ISOLATION_READ_UNCOMMITTED
* static int ISOLATION_READ_COMMITTED
* static int ISOLATION_REPEATABLE_READ
* static int ISOLATION_SERIALIZABLE
2. 事務的傳播行爲常量(不用設置,使用默認值)
* 先解釋什麼是事務的傳播行爲:解決的是業務層之間的方法調用!!
* PROPAGATION_REQUIRED(默認值) -- A中有事務,使用A中的事務.若是沒有,B就會開啓一個新的事務,將A包含進來.(保證A,B在同一個事務中),默認值!!
* PROPAGATION_SUPPORTS -- A中有事務,使用A中的事務.若是A中沒有事務.那麼B也不使用事務.
* PROPAGATION_MANDATORY -- A中有事務,使用A中的事務.若是A沒有事務.拋出異常.
* PROPAGATION_REQUIRES_NEW -- A中有事務,將A中的事務掛起.B建立一個新的事務.(保證A,B沒有在一個事務中)
* PROPAGATION_NOT_SUPPORTED -- A中有事務,將A中的事務掛起.
* PROPAGATION_NEVER -- A中有事務,拋出異常.
* PROPAGATION_NESTED -- 嵌套事務.當A執行以後,就會在這個位置設置一個保存點.若是B沒有問題.執行經過.若是B出現異常,運行客戶根據需求回滾(選擇回滾到保存點或者是最初始狀態)
Spring 框架事務管理的分類
1. Spring的編程式事務管理(不推薦使用)
* 經過手動編寫代碼的方式完成事務的管理(不推薦)
2. Spring的聲明式事務管理(底層採用AOP的技術)
* 經過一段配置的方式完成事務的管理
編程式事務管理(瞭解)
1. 說明:Spring爲了簡化事務管理的代碼:提供了模板類 TransactionTemplate,因此手動編程的方式來管理事務,只須要使用該模板類便可!!
2. 手動編程方式的具體步驟以下:
1. 步驟一:配置一個事務管理器,Spring使用PlatformTransactionManager接口來管理事務,因此我們須要使用到他的實現類!!
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2. 步驟二:配置事務管理的模板
<!-- 配置事務管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
3. 步驟三:在須要進行事務管理的類中,注入事務管理的模板
<bean id="accountService" class="com.itheima.demo1.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="transactionTemplate" ref="transactionTemplate"/>
</bean>
4. 步驟四:在業務層使用模板管理事務:
// 注入事務模板對象
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void pay(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 扣錢
accountDao.outMoney(out, money);
int a = 10/0;
// 加錢
accountDao.inMoney(in, money);
}
});
聲明式事務管理(重點)
- 基於AspectJ的XML方式(重點掌握)
- 基於AspectJ的註解方式(重點掌握)
實現
準備轉帳環境:
***業務層:
***AccountService
***AccountServiceImpl
***持久層:
***AccountDao
***AccountDaoImpl
***spring配置:
***單元測試代碼:
***配置事務管理的AOP
***平臺事務管理器:DataSourceTransactionManager
***事務通知:<tx:advice id=」」 transaction-manager=」」/>
***AOP配置:
<aop:config>
<aop:advisor advice-ref=」」 pointcut=」」/>
</aop:config>
源碼分析tx:advisor
源碼入口
此處須要瞭解TxAdviceBeanDefinitionParser的繼承體系,TxAdviceBeanDefinitionParseràAbstractSingleBeanDefinitionParseràAbstractBeanDefinitionParser,由於根據上面loadBeanDefinitions流程源碼分析,咱們知道自定義元素的解析工做是從一個namespaceHandler.parser方法開始的,該方法在AbstractBeanDefinitionParser類中
咱們重點關心如何獲取BeanDefinition對象的,因此接下來,咱們進入parseInternal方法,該方法在AbstractSingleBeanDefinitionParser中(參考上面繼承體系)
接下來,咱們來到了TxAdviceBeanDefinitionParser類,由於getBeanClass方法和doParser方法都在該類裏面
此時咱們重點了解一下TransactionInterceptor這個類,它是咱們分析的最終目標
invokeWithInTransaction方法在TransactionInterceptor類的父類TransactionAspectSupport中:
對於事務源碼,瞭解到此處基本上能夠了,若是想再瞭解事務是如何開啓和提交的,請繼續往下看,接下來咱們進入createTransactionIfNecessary方法看看,事務是如何開啓的
咱們進入AbstractPlatformTransactionManager中的getTransaction方法繼續瞭解事務是如何開啓的:
接下來,該進入doBegin方法了,不過該方法在具體的平臺事務管理器的子類中,咱們此處使用DataSourceTransactionManager子類進行源碼跟蹤:
DataSourceTransactionManager的事務管理是經過底層的JDBC代碼實現的,可是不一樣的平臺事務管理器,它們底層的事務處理也是不一樣的。
事務管理之基於AspectJ的註解方式(重點掌握)
***service類上或者方法上加註解:
***類上加@Transactional:表示該類中全部的方法都被事務管理
***方法上加@Transactional:表示只有該方法被事務管理
***開啓事務註解:
原文出處:https://www.cnblogs.com/chenyanbin/p/11832058.html