事务管理与SM整合
事务控制:
1. 注解开启事务:
- spring-service.xml中添加开启事务配置:
1 2 3 4 5 6 7 8 9 10
| <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
|
- 业务类的业务方法(对数据库的增删改方法)上添加 Transactional 开启事务:
1 2 3 4
| @Transactional public * add(..) { }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int timeout() default -1;
boolean readOnly() default false;
Isolation isolation() default Isolation.DEFAULT;
Propagation propagation() default Propagation.REQUIRED;
Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; @Transactional(rollbackFor = {FileNotFoundException.class})
Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {}; @Transactional(noRollbackFor = {ArithmeticException.class})
|
2. 事务隔离级别:
1. 概念:
- 脏读 (一定不能发生):a、b 并发访问数据库,a 在事务方法中修改 val 还未提交,b 读了 val 的值此时 a 发送错误回滚。b 读到的值是无效的,这就是脏读。
- 不可重复读 (针对字段):a 在事务方法内读取了 val,b 在此时修改了 val 的值并提交了,a 还在未完成的事务中再次读取了 val 的值。这种在一个事务中多次读取一个字段值不同的问题叫不可重复读。
- 幻读 (针对行):a 在事务中查询 >4 的 val行,b此时向表中插入或删除了几条 >4 的 val 并提交了,a在事务中再次读取时数据个数发生变化,误以为多读或者少读了数据。
2. 隔离级别:
- 读未提交(READ UNCOMMITTED)
- 读已提交 (READ COMMITTED)
- 可重复读 (REPEATABLE READ)
- 串行化 (SERIALIZABLE 几乎不可能使用)
3. 关系表:
隔离级别 |
脏读 |
不可重复读 |
幻读 |
读未提交 |
√ |
√ |
√ |
读已提交 |
× |
√ |
√ |
可重复读 |
× |
× |
√ |
串行化 |
× |
× |
× |
3. xml配置事务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <aop:config> <aop:pointcut id="pointcut" expression="execution(* ruoxijun.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/> </aop:config>
<tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes>
<tx:method name="query*" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
|
Spring 整合 MyBatis:
1. 导入整合需要的依赖:
Spring整合Mybatis除了它们自身需要的包以外,还需要mybatis和spring整合的包 mybatis-spring 包:
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency>
|
2. 新建 spring-dao.xml
对 mybatis 进行配置:
1. datasource:spring 管理数据源
1 2 3 4 5 6 7
| <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false& useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="991314"/> </bean>
|
- 通常以上属性的value值会单独配置到一个
properties
文件中方便我们管理,这时需要在配置数据源的文件中添加 <context:property-placeholder location="classpath:database.properties"/>
的配置引用外部属性文件。
- classpath :是固定写法,表示引用类路径下的一个资源。
- 在 value 中填入配置文件的值只需使用
${key}
即可,为了防止配置文件中的 key 与 spring 中自带的属性冲突通常会在数据源配置的 key 前加上 jdbc. 的前缀(如 username 就是 spring 自带的属性则我们配置文件中不能使用 username ,可以改为 jdbc.username )。
2. 在 MyBatis-Spring 中,可使用 SqlSessionFactoryBean 来创建 SqlSessionFactory 。将配置的数据源赋给 sqlSessionFactory 的 dataSource 属性
1 2 3 4 5 6 7 8
| <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/> </bean>
|
3. 在 MyBatis-Spring 中 SqlSessionTemplate 就是 SqlSession,需要利用构造函数传入我们配置好的sqlSessionFactory
1 2 3 4
| <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
|
4. 在spring的总配置文件applicationContext.xml
中导入mybatis的spring配置:
1 2
| <import resource="spring-dao.xml"/>
|
3. 编写 MapperImpl 实现类:
对数据库进行操作除编写 Mapper接口 定义方法和配置 Mapper.xml 以外,spring中新增一项那就是还需要给Mapper接口编写MapperImpl实现类:
1 2 3 4 5 6 7 8 9 10 11 12
| public class UserMapperImpl implements UserMapper { private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> getUserList() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.getUserList(); } }
|
4. 将MapperImpl实现类配置为bean:
在 applicationContext.xml
中配置MapperImpl类并将sqlSession注入:
1 2 3
| <bean id="userMapper" class="ruoxijun.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
|
5. 测试:
1 2 3
| ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = (UserMapper) context.getBean("userMapper"); List<User> userList = userMapper.getUserList();
|
扩展:
- 除4中那样直接实现Mapper接口外,还提供了一种继承SqlSessionDaoSupport类+实现Mapper接口的方式,这样我们不必再自己去获取写获取sqlsession方法,它的内部提供了**
getSqlSession
**方法获取SqlSession。
1 2 3 4 5 6
| public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper { @Override public List<User> getUserList() { return getSqlSession().getMapper(UserMapper.class).getUserList(); } }
|
在 applicationContext.xml
中配置MapperImpl的bean时,可以选择只注入sqlSessionFactory表示我们连spring-dao.xml
的第2步的第3小步也可省略:
1 2 3 4 5
| <bean id="userMapper" class="ruoxijun.mapper.UserMapperImpl"> <property name="sqlSessionTemplate" ref="sqlSession"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
|
事务管理:
spring中事务 利用 AOP 给指定方法配置事务 ,当方法中的语句出错时,那么此方法中所有有关数据库数据的操作的事务都不会提交。并且对某方法增加事务只需在 beans.xml 进行配置,不用改动原程序这正是aop的概念。
实例:在 spring-dao.xml
对 mybatis 进行新增事务配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> <property name="dataSource" ref="dataSource"/> </bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
<aop:config> <aop:pointcut id="txPointCut" expression="execution(* ruoxijun.mapper.UserMapperImpl.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
|
mybatis逆向工程(mybatis.generator):
1 2 3 4 5
| <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.4.0</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <?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="DB2Tables" targetRuntime="MyBatis3"> <commentGenerator> <property name="suppressAllComments" value="true" /> </commentGenerator> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8" userId="root" password="991314"> </jdbcConnection> <javaTypeResolver > <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <javaModelGenerator targetPackage="ruoxijun.bean" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <sqlMapGenerator targetPackage="ruoxijun.dao" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <javaClientGenerator type="XMLMAPPER" targetPackage="ruoxijun.dao" targetProject=".\src\main\java"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <table tableName="tbl_emp" domainObjectName="Employee"></table> <table tableName="tbl_dept" domainObjectName="Department"></table> </context> </generatorConfiguration>
|
1 2 3 4 5 6 7 8 9
| 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);
|