SSM整合踩坑日记

项目github地址

https://github.com/y894577/smbms

MyBatis-Spring

MyBatis-Spring 官方文档

http://mybatis.org/spring/zh/index.html

db.properties

如果将数据库信息整合在db.properties时,需要注意要加上jdbc.*

例如:

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/data?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
username=root
password=root

这种格式读取配置文件会默认访问系统变量,从而报如下错误

1
2
3
4
5
### The error may involve com.test.dao.user.UserDao.getUserCount
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException:
Failed to obtain JDBC Connection; nested exception is java.sql.SQLException:
Access denied for user 'Magic Gunner'@'localhost' (using password: YES)

正确的配置文件应该是:

1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/data?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root

mapper.xml

在引入xml的时候,applicationContext和mybatisConfig不能同时引用

applicationContext:

1
2
3
4
5
6
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/test/dao/*/*.xml"/>
<property name="configLocation" value="mybatis-config.xml"/>
</bean>

mybatisConfig:

1
2
3
4
5
6
<mappers>
<mapper resource="com/test/dao/user/UserMapper.xml"/>
<mapper resource="com/test/dao/provider/ProviderMapper.xml"/>
<mapper resource="com/test/dao/role/RoleMapper.xml"/>
<mapper resource="com/test/dao/bill/BillMapper.xml"/>
</mappers>

我们在创建工厂bean的时候,property已经引入了mapperLocations,如果mybatisConfig引入的话,会报错

1
2
3
4
5
6
7
8
9
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' 
defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is
org.springframework.core.NestedIOException: Failed to parse mapping resource: 'file
[E:\StudyBook\java2\target\classes\com\test\dao\bill\BillMapper.xml]'; nested exception is
org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'file
[E:\StudyBook\java2\target\classes\com\test\dao\bill\BillMapper.xml]'. Cause:
java.lang.IllegalArgumentException: Mapped Statements collection already contains value for
com.test.dao.bill.BillDao.getBillCountByProId. please check com/test/dao/bill/BillMapper.xml
and file [E:\StudyBook\java2\target\classes\com\test\dao\bill\BillMapper.xml]

sqlSession.close()

一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。

一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。

事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。

在Spring托管的SqlSession上不允许手动关闭

1
2
java.lang.UnsupportedOperationException: Manual close is not allowed over a Spring managed SqlSession
at org.mybatis.spring.SqlSessionTemplate.close(SqlSessionTemplate.java:359)

//终于不用手动关闭了,把泪目打在公屏上


context.getBean

单例模式(singleton): 创建IOC容器时即创建bean对象,以后每次取值都是取的这个bean对象

原型模式(protortype): 创建IOC容器时不创建bean对象,以后每次取值时再分别创建

如果在同一个context里要创建多个bean,需要将scope改为prototype,因为bean默认是singleton的(详见单例模式)

1
2
3
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserServiceImpl userService = (UserServiceImpl) context.getBean("UserServiceImpl");
RoleServiceImpl roleService = (RoleServiceImpl) context.getBean("RoleServiceImpl");

这个时候两个创建都是调用的同一个实例,第二个实例初始化会失败

应该改为:

1
2
3
<bean id="UserServiceImpl" class="com.test.service.user.UserServiceImpl" scope="prototype">
<property name="sqlSession" ref="sqlSession"/>
</bean>

PageHelper

PageHelper官方文档

https://pagehelper.github.io/

在使用PageHelper的时候,当使用PageHelper.startPage 静态方法调用时,需要注意是方法后的第一个查询才会生效。

在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。

我在BillService进行分页的时候,发现有时候分页会生效有时候不会,查看服务器日志发现,由于我前端发了两个http请求,一个用于查询Bill,一个用于查询Provider,而该方法为静态方法,因此我的分页请求应该是作用在了查询Provider上,而我只需要Bill分页。

解决方法:使用ISelect接口

1
2
3
4
5
pageInfo = PageHelper.startPage(currentPageNo, Constant.PAGESIZE).doSelectPageInfo(new ISelect() {
public void doSelect() {
billMapper.getBillList(finalProductName, finalProviderId, finalIsPayment);
}
});
文章目录
  1. 1. MyBatis-Spring
    1. 1.1. db.properties
    2. 1.2. mapper.xml
    3. 1.3. sqlSession.close()
    4. 1.4. context.getBean
  2. 2. PageHelper
|