Spring事務管理實現篇
阿新 • • 發佈:2020-12-19
實現使用的maven工程,程式碼有點多,耐心看完~
1.事務概念:
事務: 是邏輯上一組操作,要麼全都成功,要麼全都失敗.
事物目的就是解決【資料不一致】的問題。
2.事務特性:
2.1.ACID:
原子性: 事務不可分割
一致性: 事務執行的前後,資料完整性保持一致.
隔離性: 一個事務執行的時候,不應該受到其他事務的打擾
永續性: 一旦結束,資料就永久的儲存到資料庫.
2.事務3類讀問題
如果不考慮隔離性,事務由3類讀問題
- 髒讀: 一個事務讀到另一個事務未提交資料
- 不可重複讀 :一個事務讀到另一個事務已經提交資料(update)導致一個事務多次查詢結果不一致
- 虛讀 :一個事務讀到另一個事務已經提交資料(insert)導致一個事務多次查詢結果不一致
事務解決併發操作3類讀問題:設定隔離級別
併發訪問丟失更新的問題:Java(鎖)/資料庫(鎖)
spring事務的實現:
1.使用MyBatis逆向工程建立資料庫的Mapper和pojo類
<generatorConfiguration> <context id="testTables" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自動生成的註釋 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--資料庫連線的資訊:驅動類、連線地址、使用者名稱、密碼 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/new" userId="root" password="123456"> </jdbcConnection> <!-- 預設false,把JDBC DECIMAL 和 NUMERIC 型別解析為 Integer,為 true時把JDBC DECIMAL 和 NUMERIC 型別解析為java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO類的位置 --> <javaModelGenerator targetPackage="pojo" targetProject=".\src"> <!-- enableSubPackages:是否讓schema作為包的字尾 --> <property name="enableSubPackages" value="false" /> <!-- 從資料庫返回的值被清理前後的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper對映檔案生成的位置 --> <sqlMapGenerator targetPackage="mapper" targetProject=".\src"> <!-- enableSubPackages:是否讓schema作為包的字尾 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper介面生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="mapper" targetProject=".\src"> <!-- enableSubPackages:是否讓schema作為包的字尾 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <table tableName="userinfo" domainObjectName="User" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"></table> </context> </generatorConfiguration>
生成方法:
public static void main(String[] args) throws Exception{ List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("E:\\檔案\\Test\\src\\main\\resources\\generator.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); }
2.匯入相關maven依賴, 有點多複製過去就好了
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.3.2</version>
<scope>optional</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>5.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jms -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-messaging -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>5.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-oxm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.dbcp</artifactId>
<version>1.2.2.osgi</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.pool</artifactId>
<version>1.5.3</version>
</dependency>
<!--<dependency>-->
3.建立業務層(Services)
方法實現程式碼
@Component
public class UserServiceImpl implements UserService {
public static SqlSession getSqlSession(){
String resource = "SqlMapConfig.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// 2、根據配置檔案建立SqlSessionFactory
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory.openSession();
}
@Override
public boolean giveMoney(int one, int two, int money) {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//轉賬人資訊
User user = mapper.selectByPrimaryKey(one);
int i = 0;
if (user!=null){
//設定轉賬金額
user.setMoney(user.getMoney()-money);
//受影響得行數
i=mapper.updateByPrimaryKey(user);
}
//轉賬人資訊
User user1 = mapper.selectByPrimaryKey(two);
int j = 0;
if (user1!=null){
//設定轉賬金額
user1.setMoney(user1.getMoney()+money);
j = mapper.updateByPrimaryKey(user1);
}
if (i>0&&j>0){
System.out.println("轉賬成功");
sqlSession.commit();
return true;
}else {
System.out.println("轉賬失敗");
sqlSession.rollback();
return false;
}
}
}
4.建立Sqlsession的工具類
public class SqlSessionUtil {
public static SqlSession getSession(){
String resource = "SqlMapConfig.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory.openSession();
}
}
5.編寫Spring的事務管理器
public class TransactionManager implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
SqlSession sqlSession = SqlSessionUtil.getSession();
//呼叫目標方法
boolean result = (boolean) methodInvocation.proceed();
if (result){
sqlSession.commit();
System.out.println("****提交事務****");
}else {
sqlSession.rollback();
System.out.println("****回滾事務****");
}
return result;
}
}
最後我們就是配置XML檔案啦!
1.beans.xml配置
<!-- 1、啟動註解掃描-->
<context:annotation-config/>
<context:component-scan base-package="service"></context:component-scan>
<!-- 1)目標 -->
<bean id="target" class="service.Impl.UserServiceImpl"></bean>
<!-- 2)事務 -->
<bean id="transactionManager" class="proxy.TransactionManager"></bean>
<!--3)代理 -->
<bean id="userInfoServiceImpl" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="service.UserService"></property>
<!-- 1)注入目標物件 -->
<property name="target" ref="target"/>
<!-- 2)事務物件 -->
<property name="interceptorNames">
<array>
<value>transactionManager</value>
</array>
</property>
</bean>
</beans>
2.database.properties(資料庫連線字串)
url=jdbc:mysql://localhost:3306/new(記得改自己的)?useUnicode=true&&characterEncoding=utf-8
driver=com.mysql.jdbc.Driver
uname=root
password=123456(記得改)
3.beans-datasource.xml配置
<!--1、載入資料庫的配置資訊 -->
<context:property-placeholder
location="database.properties" />
<!--2、datasource資料來源 -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${uname}" />
<property name="password" value="${password}" />
</bean>
<!-- 3、sqlSessionFactory -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 別名 -->
<property name="typeAliasesPackage" value="pojo"></property>
<!-- mapper XML對映 -->
<property name="mapperLocations"
value="classpath*:mapper/*Mapper.xml"></property>
<!-- 資料來源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--4、mapper介面的位置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper"></property>
</bean>
<!-- 1)、事務管理(增強/通知):事務 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/> </bean>
4.SqlMapConfig.xml配置
<configuration>
<typeAliases>
<package name="pojo"/>
</typeAliases>
<plugins>
<!-- com.github.pagehelper為PageHelper類所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
<!-- 配置mybatis的環境資訊 -->
<environments default="development">
<environment id="development">
<!-- 配置JDBC事務控制,由mybatis進行管理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置資料來源,採用dbcp連線池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/new?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 載入mapper -->
<mappers>
<package name="mapper"/>
</mappers>
</configuration>
腳下留心:記得在實現類裡面加上註解
最後我們就可以寫實測試類了
1.事務回滾:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans.xml"})
public class Test2 {
@Qualifier("userInfoServiceImpl")
@Autowired
UserService userService;
@Test
public void Test(){
boolean b = userService.giveMoney(4, 2, 100);
System.out.println(b);
}
}
由於資料庫沒有id為4,所所以轉賬失敗
2.事務提交:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans.xml"})
public class Test2 {
@Qualifier("userInfoServiceImpl")
@Autowired
UserService userService;
@Test
public void Test(){
boolean b = userService.giveMoney(1, 2, 100);
System.out.println(b);
}
}
轉賬成功!這裡我們看到資料庫發生了改變~