案例:
甲乙两个人转钱,正常情况下:甲输入乙的账号,输入金额,然后甲的账户扣除相应的钱,乙的账户扣除相应的钱;
但是就是有特殊情况,甲在输入账号的时候输错了,就会发生一个情况,甲的钱扣掉了,但是乙没有收到钱,肯定就会出现问题;这就是事务相关的问题:
解决办法:
双方进行转账时其中一方发生异常,及时进行回滚,等待数据正常时在进行提交
一、创建进行转账的账号数据库
CREATE TABLE userinfo
(
id
int(11) NOT NULL AUTO_INCREMENT,
username
varchar(255) DEFAULT NULL,
birthday
datetime DEFAULT NULL,
sex
varchar(1) DEFAULT NULL,
address
varchar(255) DEFAULT NULL,
money
int(255) DEFAULT NULL,
PRIMARY KEY (id
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
插入数据
INSERT INTO userinfo
VALUES (1, ‘旺财’, ‘2020-09-29 00:00:00’, ‘女’, ‘五一广场’, 2100);
INSERT INTO userinfo
VALUES (2, ‘来福’, ‘2020-09-16 00:00:00’, ‘男’, ‘五一新干线’, 700);
COMMIT;
主键:SET FOREIGN_KEY_CHECKS = 1;
二、导入相关包
<dependencies> <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>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.3</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>net.sf.ezmorph</groupId> <artifactId>ezmorph</artifactId> <version>1.0.6</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.41</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
<dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.2.3</version> <classifier>jdk15</classifier><!-- 指定jdk版本 --> </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>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.2</version> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.3.2</version> </dependency>
<!-- 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>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.3</version> </dependency>
</dependencies>
|
|
|
|
三、创建一个转账的接口
public interface Changmoney {
boolean giveMoney(int on,int to,int moeny);
}
|
|
|
|
四、xml配置
一.SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>
<settings> <!--<setting name="lazyLoadingEnabled" value="true"/>--> <!--<setting name="aggressiveLazyLoading" value="false"/>--> <!-- 开启二级缓存总开关 --> <setting name="cacheEnabled" value="true"/> <setting name="defaultStatementTimeout" value="25"/> </settings> <typeAliases> <package name="cc.entity"/> </typeAliases>
<!-- 配置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://192.168.244.35:3306/mybatis?useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource> </environment> </environments> <!-- 加载mapper --> <mappers> <!--<mapper resource="sqlmap/User.xml"/>--> <package name="cc.mapper"/> </mappers>
</configuration>
|
二.创建generator.xml,用mybatis-generator自动生成器数据库简单语句、接口、实现类等
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<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/t142" userId="root" password="123456"> </jdbcConnection> <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg" password="yycg"> </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="cc.mapper" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="cc.mapper" targetProject=".\src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!--指定数据库表--> <table tableName="userinfo" domainObjectName="Userinfo" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"></table>
<!--<table tableName="test" domainObjectName="Test" 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("D:\\IDe\\Springzhuangz\\src\\config\\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); }
|
|
|
|
四.创建实现类
public class ChangeMoneyImpl implements Changmoney {
@Override public boolean giveMoney(int on, int to, int moeny) {
SqlSession sqlSession = SqlSessionUtil.openSession();
UserinfoMapper mapper = sqlSession.getMapper(UserinfoMapper.class);
//转钱人信息 Userinfo userinfo = mapper.selectByPrimaryKey(on); int i=0;
if (userinfo!=null){
//设置转钱人的余额 userinfo.setMoney(userinfo.getMoney()-moeny);
i = mapper.updateByPrimaryKey(userinfo);
}
//设置被转钱的信息 Userinfo userinfo1 = mapper.selectByPrimaryKey(to); int j=0;
if (userinfo1!=null){
//设置被转钱人的余额 userinfo1.setMoney(userinfo1.getMoney()+moeny);
//受影响的行数 j = mapper.updateByPrimaryKey(userinfo1); }
if (i>0&&j>0){ System.out.println("转钱成功"); //sqlSession.commit(); return true; }else { System.out.println("转钱失败"); //sqlSession.rollback(); return false; }
}
}
|
|
|
|
五.创建SqlSessionFactory工厂
public class SqlSessionUtil { private static SqlSession sqlSession= null; static { //1、根据mybaits的配置文件构建SqlSessionFactory //需要改成我们自己的SqlMapConfig.xml的路径 String resource = "SqlMapConfig.xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2、创建SqlSession sqlSession= sqlSessionFactory.openSession(); } public static SqlSession openSession() { return sqlSession; }
public static void main(String[] args) { System.out.println(SqlSessionUtil.openSession()); } }
|
|
|
|
六.测试SqlSessionFactory
测试类+结果
public class TestZhuangz {
public static void main(String[] args) {
Changmoney changmoney = new ChangeMoneyImpl();
boolean b = changmoney.giveMoney(1, 2, 200); System.out.println(b);
}
}
|
|
|
|
七.配置代理事务,配置数据库连接、beans.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--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="${username2}"/> <property name="password" value="${password}"/> </bean> <!-- 3、sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 别名 --> <property name="typeAliasesPackage" value="com.cc.model"></property> <!-- mapper XML映射 --> <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"></property> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> </bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.cc.mapper"></property> </bean>
<!-- 5、事务管理 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
</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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd "> <!-- 1、启动注解扫描--> <context:annotation-config/> <context:component-scan base-package="cc"></context:component-scan>
<!-- 1)目标 --> <bean id="target" class="cc.change.ChangeMoneyImpl"></bean> <!-- 2)黑客 --> <bean id="transactionManager" class="cc.proxy.TransactionManager"></bean> <!--3)代理 -->
<bean id="ChangeMoneyImpProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="cc.change.Changmoney"></property> <!-- 1)注入目标对象 --> <property name="target" ref="target"/> <!-- 2)黑客对象 --> <property name="interceptorNames"> <array> <value>transactionManager</value> </array> </property> </bean>
</beans>
|
|
|
|
八.编写实体代理类
public class TransactionManager implements MethodInterceptor{
@Override public Object invoke(MethodInvocation method) throws Throwable {
SqlSession sqlSession = SqlSessionUtil.openSession(); //调用目标方法 boolean result = (boolean) method.proceed(); if( method.getMethod().getName().equals("giveMoney")){ if(result) { sqlSession.commit(); System.out.println("====提交事务==="); }else { sqlSession.rollback(); System.out.println("====回滚事务==="); } } return result; }
}
|
|
|
|
九.测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:beans.xml"}) public class Test2 { @Autowired @Qualifier("ChangeMoneyImplProxy") changmoney changmoney; @Test public void test1(){ boolean b = changmoney.giveMoney(1, 2, 30); System.out.println(b); } }
|
|
|
|
实现