所要完成的功能—转账(这里使用的是maven工程)
案例:甲乙两个人转钱,正常情况下:甲输入乙的账号,输入金额,然后甲的账户扣除相应的钱,乙的账户扣除相应的钱;
但是就是有特殊情况,甲在输入账号的时候输错了,就会发生一个情况,甲的钱扣掉了,但是乙没有收到钱,肯定就会出现问题;这就是事务相关的问题:
解决办法:但双方进行转账时其中一方发生异常,及时进行回滚,等待数据正常时在进行提交
一、创建进行转账的账号数据库
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;
插入数据:BEGIN;
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;
二、
在这之前大家记得导入相关架包(因为使用的是maven空间,大家架包可以去maven中央仓库去下载
三、创建一个转账的接口
四、配制数据库连接
<?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"/>
</settings>
<typeAliases>
<package name="pojo"/>
<package name="vo"/>
</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://localhost:3306/userinfo?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 加载mapper -->
<mappers>
<!--<mapper resource="sqlmap/User.xml"/>-->
<package name="mapper"/>
</mappers>
</configuration>
五、使用MyBateis来生成数据库相关语句、接口、实现类等
<?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/userinfo"
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="po"
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="Userinfo"
enableCountByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
enableUpdateByExample="false"/>
</context>
</generatorConfiguration>
然后用测试类来生成接口和实现类(记得修改包的相关路径)
@Test
public void testMBG() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("mbg.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);
}
}
生成了之后大家记得改变包的位置不然怕加载不了
六、创建实现类
@Configuration
public class ChangeMoneyImpl implements changmoney {
@Override
public boolean giveMoney(int on, int to, int money) {
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()-money);
//收影响的行数
i = mapper.updateByPrimaryKey(userinfo);
}
//被转钱人的信息
Userinfo userinfo2 =mapper .selectByPrimaryKey(to);
int j=0;
if (userinfo2!=null){
//设置转钱人的余额
userinfo2.setMoney(userinfo2.getMoney()+money);
//收影响的行数
j = mapper.updateByPrimaryKey(userinfo2);
}
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
//需要改成我们自己的mybatis-config.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;
}
}
测试是可以转转成功的
接下来配置代理事务了
首先配置数据库连接
<?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>
url=jdbc:mysql://localhost:3306/userinfo?useUnicode=true&&characterEncoding=utf-8
driver=com.mysql.jdbc.Driver
username2=root
password=123456
接下来就是配置事务代理
<?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="change"></context:component-scan>
<!-- 1)目标 -->
<bean id="target" class="change.ChangeMoneyImpl"></bean>
<!-- 2)黑客 -->
<bean id="transactionManager" class="proxy.TransactionManager"></bean>
<!--3)代理 -->
<bean id="ChangeMoneyImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="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(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);
}
}
所得结果为