SpringMVC整合Mybatis
步骤:
- 导入相关依赖
- Spring核心依赖
- Spring-MVC核心依赖
- Mybatis核心依赖
- MySQL数据库驱动依赖
- 注意Mysql的版本和驱动版本保持一致
- Druid数据库连接池依赖
- MySQL数据库驱动依赖
- Spring-Mybatis整合依赖
- 会自动导入Spring-mabtis、Spring-jdbc和spring-tx依赖
<dependencies><!--Spring核心依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.2.RELEASE</version></dependency><!--SpringMVC核心依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.2.RELEASE</version></dependency><!--整合Mybatis--><!--1. Mysql数据库驱动依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!--2. 连接池依赖--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.23</version></dependency><!--3. mybatis本身的依赖--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><!--4. 整合mybatis和spring的依赖--><!--MyBatis提供的和Spring进行整合的jar包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version></dependency><!--spring对jdbc封装的jar包也要导入进来,否则mybatis无法整合--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.2.RELEASE</version></dependency><!--编译时DispatcherServlet的依赖--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency>
-
在配置文件中配置数据库连接信息jdbc.properties
-
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/learn?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true db-username=root password=root
-
-
编写mapper接口实现对数据库操作
-
@Mapper注解可加可不加
-
@Mapper public interface UserMapper {@Select("select * from user where id=#{id}")User getUserById(int id);@Select("select * from user")List<User> getAllUser();}
-
-
在Spring容器配置类中扫描mapper接口所在的包
-
并通过配置文件创建数据源和sqlSession工厂,将mybatis相关对象交给spring容器进行管理
-
@Configuration @ComponentScan(basePackages = {"server"}) @MapperScan(basePackages = "mapper") @PropertySource("classpath:jdbc.properties") public class SpringConfig {@Value("${driver}")String driver;@Value("${url}")String url;/*这里在测试的时候使用的是username,不过username被解析为默认值即电脑名称而不是配置文件中配置的值原因如下:username 和 password 被系统属性或环境变量覆盖,如果系统属性或环境变量中存在 username,Spring 会优先使用这些值,而不是从 jdbc.properties 中读取。*/@Value("${db-username}")String username;@Value("${password}")String password;@Beanpublic DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();// 基本配置dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Bean@Autowiredpublic SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource){SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();sqlSessionFactory.setDataSource(dataSource);/*加载xml文件的位置// 指定 Mapper XML 文件的位置sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));// 调用 getObject() 返回 SqlSessionFactory 实例return sessionFactory.getObject();*/return sqlSessionFactory;}}
-
-
在Server相关类中注入mapper接口,调用mapper接口的方法完成对数据库的操作
-
@Service public class UserServer {@AutowiredUserMapper userMapper;public String getUser(){User user=userMapper.getUserById(1);String str=user.toString();return str;}}
-
事务管理
默认行为:
- JDBC 连接的默认行为
- 在 JDBC 中,连接的默认行为是 自动提交事务(
autoCommit=true
)。 - 这意味着,如果没有显式地开启事务,每个 SQL 语句都会立即提交到数据库。
- 在 JDBC 中,连接的默认行为是 自动提交事务(
- Spring 整合 MyBatis 的情况
- 如果没有配置任何事务管理器(如
DataSourceTransactionManager
),Spring 不会介入事务管理。 - MyBatis 会直接使用底层的 JDBC 连接,因此每个 SQL 语句会立即提交。
- 如果没有配置任何事务管理器(如
- 如果配置了事务管理器,确保事务管理器和mybatis使用的是统一个数据库源
- 共享数据源:事务管理器和 MyBatis 通常共享同一个数据源。这样,事务管理器可以管理 MyBatis 使用的数据库连接,确保事务的一致性。
- 事务管理器的优先级:事务管理器会覆盖 MyBatis 的默认事务行为。如果配置了事务管理器,MyBatis 会使用事务管理器管理的事务,而不是 MyBatis 的默认行为。
配置事务管理器:
-
将事务管理器Bean配置在Spring容器中:
-
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource); }
-
-
启动注解驱动
-
区分spring-config和web-config场景需要注意
- 注意配置该注解驱动上的配置类中,配置的包扫描中要能扫到
@Transactional
注解
- 注意配置该注解驱动上的配置类中,配置的包扫描中要能扫到
-
@Configuration @EnableTransactionManagement public class AppConfig { }
-
-
使用
@Transactional
注解管理事务-
@Service public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic void createUser(User user) {userMapper.insert(user);throw new RuntimeException("Test exception"); // 抛出异常} }
-
事务传播行为
-
默认行为
-
默认的传播行为是
REQUIRED
,即如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。 -
@Transactional public void methodA() {// 开启事务methodB(); // 调用方法 B// 提交或回滚事务 }@Transactional(propagation = Propagation.REQUIRED) public void methodB() {// 在方法 A 的事务中执行 }
-
-
嵌套事务
- 如果方法 A 调用方法 B,且方法 B 的传播行为是
REQUIRES_NEW
,则方法 B 会开启一个新事务,方法 A 的事务会被挂起。 - 如果方法 B 的传播行为是
NESTED
,则方法 B 会在方法 A 的事务中创建一个嵌套事务。 REQUIRES_NEW
创建的新事务是独立的,如果方法 B 回滚,不会影响方法 A 的事务。
- 如果方法 A 调用方法 B,且方法 B 的传播行为是
事务超时行为
-
默认行为
- 默认情况下,事务没有超时限制。
-
设置超时
-
可以通过
@Transactional
注解的timeout
属性设置事务的超时时间(单位:秒)。 -
@Transactional(timeout = 10) public void updateUser(User user) {userMapper.update(user); }
-
超时回滚:如果事务在超时时间内未完成,Spring 会回滚事务。
-
性能影响:设置合理的超时时间,避免长时间占用数据库资源。
-
事务的只读属性
-
默认行为
- 默认情况下,事务是可读写的。
-
设置只读
-
可以通过
@Transactional
注解的readOnly
属性将事务设置为只读。 -
@Transactional(readOnly = true) public User getUserById(int id) {return userMapper.selectById(id); }
-
性能优化:只读事务可以优化数据库的性能,因为数据库不需要为写操作维护锁。
-
写操作禁止:在只读事务中执行写操作会抛出异常。
-
事务的失效场景
-
@Transactional
注解只能用于 public 方法。如果将其应用于非 public 方法(如protected
、private
或包级私有方法),事务不会生效。- 确保
@Transactional
注解仅用于 public 方法。
- 确保
-
如果方法 A 调用同一个类中的方法 B,且方法 B 有
@Transactional
注解,事务不会生效。这是因为 Spring 的事务管理是基于代理的,自调用不会经过代理。-
@Service public class UserService {public void createUser(User user) {insertUser(user); // 自调用,事务失效}@Transactionalpublic void insertUser(User user) {userMapper.insert(user);} }
-
解决方法
-
将方法 B 移到另一个类中:
-
@Service public class UserService {@Autowiredprivate UserHelper userHelper;public void createUser(User user) {userHelper.insertUser(user); // 通过依赖注入调用} }@Service public class UserHelper {@Autowiredprivate UserMapper userMapper;@Transactionalpublic void insertUser(User user) {userMapper.insert(user);} }
-
-
使用
AopContext.currentProxy()
:-
@Service public class UserService {public void createUser(User user) {((UserService) AopContext.currentProxy()).insertUser(user); // 通过代理调用}@Transactionalpublic void insertUser(User user) {userMapper.insert(user);} }
-
-
-
-
如果在事务方法中捕获了异常但没有重新抛出,事务不会回滚。默认情况下,Spring 只会在遇到运行时异常(
RuntimeException
)时回滚事务。- 在捕获异常后重新抛出,或使用
@Transactional
注解的rollbackFor
属性指定回滚的异常类型。
- 在捕获异常后重新抛出,或使用
-
如果事务方法被
final
或static
修饰,事务不会生效。这是因为 Spring 的事务管理是基于动态代理的,无法代理final
或static
方法。- JDK 动态代理基于接口实现,要求目标类必须实现至少一个接口。
- CGLIB 动态代理基于继承实现,通过生成目标类的子类来创建代理对象。
- 被代理的方法要求是从接口中实现的方法或者可以继承的方法,所以final方法无法被代理
- static方法是类级别的,所以无法被代理
Junit
作用:
- 自动加载Spring容器而无需手动加载
- 运行demo方法
流程:
-
导入依赖
-
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.1.5.RELEASE</version> </dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope> </dependency>
-
-
编写测试类,通过注解指定Spring容器的位置
-
通过自动注入导入Spring容器中的Bean即可测试
-
注意测试方法需要@Test注解
-
@RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration(value = {"classpath:applicationContext.xml"}) // 指定配置文件 @ContextConfiguration(classes = SpringConfig.class) // 指定配置类 public class junitTest {@Autowiredpublic UserServer userServer;@Testpublic void test(){String user = userServer.getUser();System.out.println(user);} }
-
Slf4j
作用:
- 日志框架
流程:
-
导入相关依赖
-
<!-- Log4j2核心库 --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.1</version> </dependency> <!-- Log4j2 API --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.14.1</version> </dependency><!-- 可选:SLF4J 绑定 --> <dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.14.1</version> </dependency>
-
创建logger对象,该对象不需要交给Spring容器进行管理
-
import org.slf4j.Logger; import org.slf4j.LoggerFactory;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) // 指定配置类 public class junitTest {private static final Logger logger = LoggerFactory.getLogger(junitTest.class);@Autowiredpublic UserServer userServer;@Testpublic void test(){String user = userServer.getUser();System.out.println(user);logger.debug("This is a debug message.");}}
-
注意:导入的一定是org.slf4j包下的相关组件
-