IOC DI
把mybatis数据源
交给Spring管理
把mybatis的核心对象SqlSessionFactory
交给Spring管理
把mapper代理对象
交给Spring管理,service的bean
交给Spring管理,在service层
AOP
基于AOP核心 Spring提供声明式事务管理器,进行事务控制,利用AOP把事务控制增强到业务层方法上
Spring与web整合:当web服务器启动的时候,帮我们创建Spring容器
Servlet不能交给Spring管理 只能手动getBean()
解决:SpringMVC替换servlet
整合步骤:
-
创建maven的web项目
maven如何创建web项目 更多细节请看下一篇——Spring与web整合- 导入依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <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>org.example</groupId> <artifactId>spring-webDemo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>spring-webDemo Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <!-- 设置编码格式 指定jdk的版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.2.15.RELEASE</spring.version> </properties> <dependencies> <!--核心容器 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!--aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <!--aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!--测试 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!--spring-web--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <!--数据访问层的相关依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!--与第三方orm框架整合的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <!--事务--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--mybatis与spring整合的依赖 优先选择2的版本--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!--数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <!--数据库连接池 c3p0 druid --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!-- web相关 --> <!-- servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <!-- 文件上传 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <!-- fastJson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.0</version> </dependency> <!--测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--log4j--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.36</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </dependency> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> </dependencies> <build> <finalName>spring-webDemo</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> <!--maven 默认把src/main/resources下所有的文件拷贝到target目录的classes目录 但没有java下的xml或properties文件 --> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>true</filtering> </resource> </resources> </build> </project>
-
导入配置
spring的配置文件
mybatis的配置: 全局配置文件: mybatis-config.xml , sql映射文件
数据库四大参数文件: db.properties
日志配置文件: log4j.properties
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X0bQIjwy-1670499551776)(D:\PracticalTrainning3\note\第二十二周\assets\1670499089433.png)]
-
编写配置文件
Spring的配置文件
applicationContext.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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.fs"/> <!--加载properties 如果是多个properties文件, 文件路径与文件路径使用逗号分割 classpath:db.properties,classpath:log4j.properties location开头 加classpath 包含resource :不用加classpath --> <context:property-placeholder location="classpath:db.properties" file-encoding="UTF-8"/> <!--数据源--> <!--配置SqlSessionFactoryBean--> <!--配置mapper扫描器 扫描指定包下所有的Mapper接口 创建Mapper接口代理对象, sqlSession.getMapper()并且保存到Spring容器--> <import resource="beans-mapper.xml"/> <!-- 事务--> <import resource="beans-tx.xml"/> </beans>
bean-mapper.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 连接池数据源--> <!--配置一个C3p0数据源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--注入属性的值 数据库四大参数--> <property name="driverClass" value="${jdbc.driverClass}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> <!--连接池相关的参数--> <!--初始连接数--> <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/> <!--增量, 每次创建几个连接--> <property name="acquireIncrement" value="${jdbc.acquireIncrement}"/> <!--最大连接数--> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"/> <!--最小连接数 默认值等于初始连接数--> <property name="minPoolSize" value="${jdbc.minPoolSize}"/> </bean> <!-- sqlSessionFactoryBean--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--注入数据源--> <property name="dataSource" ref="dataSource"/> <!--加载mybatis全局配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!--配置别名--> <property name="typeAliasesPackage" value="com.fs.shop.pojo"/> <!--加载sql映射文件 * 任意 一定要注意: classpath*: 如果不写, 后面*无效--> <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/> </bean> <!-- mapper 扫描mapper接口,生成代理类 交给spring管理--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--扫描那个包下Mapper接口--> <property name="basePackage" value="com.fs.shop.mapper"/> <!--注入SqlSessionFactory--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> </bean> </beans>
bean-tx.xml
…
-
声明式事务
如果加了事务 有异常 就会回滚 不会对数据库进行操作
如果没加事务(在非事务下执行代码) 即使抛了异常 但还是会对数据库进行操作
Spring提供两种事务管理:
- Spring提供了声明式事务管理: 底层使用AOP
- 编程式事务管理
提供一个事务管理器: PlatformTransactionManager(接口类型,位于spring-tx的jar), 配置一个事务管理器
使用的实现类:
- 基于jdbc的事务管理器: DataSourceTransactionManager (类) 位于spring-jdbc的jar, Mybatis使用这个事务管理器
- 基于hibernate的事务管理器: HibernateTransactionManager(类),位于Hibernate的jar
TransactionDefinition 定义事务信息 事务可以配置什么
隔离级别 传播行为 超出时间 只读 事务策略
事务四大特征: ACID
原子性 隔离性 一致性 持久性
回滚点?事务是什么?为什么要开启事务?
假设A银行存钱,B同时去银行取钱,此时银行账户只有这100块钱,为了保证事务的原子性,不可能出现B取不到钱,而A却成功存了钱,开启事务,发生异常的时候就回滚,如果A存钱失败了,B也不能取钱(回到取多少钱的页面)。
A存钱,B取钱相当于两个sql语句,一起执行,有时候会失败,所以需要事务。
rollBack() 如果没有回滚点 从结束到开始 回滚一遍
isCompleted 事务是否完成 事务提交、回滚就结束
开启事务
-
在bean-tx.xml配置
<!--事务--> <!--配置一个事务管理器--> <!--注入一个DataSource的属性--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 1. 配置xml文件 配置一个事务通知 如果事务管理器id为transactionManager(默认值) 就不用写 transaction-manager="transactionManager" --> <tx:advice id="txAdvice"> <!--事务策略 --> <tx:attributes> <!--属性 name:方法名 支持*通配符 propagation 事务传播机制 默认值 REQUIRED isolation 事务隔离级别 默认值 DEFAULT 表示当前事务与其他事务的隔离程度 ACID 事务四大特征 原子性 隔离性 一致性 持久性 timeout: 超时时间 默认值 -1 永不超时 单位:秒 read-only 默认值false 增删改不能设置为只读 rollback-for 什么情况下回滚 no-rollback-for 什么情况下不回滚 --> <tx:method name="query*" read-only="true" propagation="SUPPORTS" /> <tx:method name="select*" read-only="true" propagation="SUPPORTS" /> <tx:method name="find*" read-only="true" propagation="SUPPORTS" /> <!-- 增删改--> <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" /> <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception" /> <tx:method name="del*" propagation="REQUIRED" rollback-for="java.lang.Exception" /> </tx:attributes> </tx:advice> <!--织入 --> <aop:config> <aop:pointcut id="pointcut1" expression="execution(* com.fs.shop..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config>
propagation事务传播机制: 默认: REQUIRED
- REQUIRED:默认值,表示如果存在一个事务,则支持当前事务,如果当前没有事务,则开启一个新的事务. 增删改
- SUPPORTS:表示如果存在一个事务,则支持当前事务,如果当前没有事务,则按非事务方式执行, 查询
-
在方法上提供事务注解:
@Transactional
<!--2. 使用注解 开启事务注解 同样也需要事务管理器--> <tx:annotation-driven/>
@Override @Transactional(rollbackFor = Exception.class)//表示该方法是在事务管理下执行 public void saveUser(User user) throws SQLException { userMapper.insert(user); if(true){ throw new SQLException("测试"); } }
非事务方法调用事务方法 1. 注解 2. 得到代理对象来调用事务方法 但是要expose-proxy
-
在方法上提供事务注解:
@Transactional
<!--2. 使用注解 开启事务注解 同样也需要事务管理器--> <tx:annotation-driven/>
@Override @Transactional(rollbackFor = Exception.class)//表示该方法是在事务管理下执行 public void saveUser(User user) throws SQLException { userMapper.insert(user); if(true){ throw new SQLException("测试"); } }
非事务方法调用事务方法
出现 Closing non transactional SqlSession 导致事务失败问题
解决:1. 在类上加全局注解 @Transactional
2. 得到当前类代理对象来调用事务方法 但是要在beans.xml配置exposeProxy=true 用于控制AOP框架公开代理,公开后才可以通过AopContext获取到当前代理类。
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
基于注解的配置
@EnableAspectJAutoProxy(proxyTargteClass = true, exposeProxy = true)
1.使用 ApplicationContext 上下文对象获取该对象;
2.使用 AopContext.currentProxy() 获取代理对象
UserMapper currentProxy =(UserMapper)AopContext.currentProxy();
currentProxy.queryById(1);