Spring和myBatis整合过程
目录
企业开发中需处理的问题
- 需要使用第三方性能优秀的数据库连接池
- 设置MyBatis,开启缓存; 其它大部分的配置工作会交接给Spring(mybatis-spring整合包)
- 管理mapper:
UserMapper(接口,Dao) -> 配置SQL -> SqlSession->mapper
UserMapper->crud
整合思路
- 新建一个maven工程(web工程,springmvc)
- 把mybatis中连接属性DataSource和数据库连接池的管理交给Spring
- 连接属性DataSource和数据库连接池是第三方插件已经完善了,我们需要借力(c3p0/dhcp/druid) , 采用druid实现数据源,再由Spring管理
- 让spring接管myBatis对于mapper的管理 , 通过spring和mybatis的整合包实现
- 为了实现SqlSessionFactory , 通过spring和mybatis的整合包实现SqlSessionFactoryBean的管理
目标:
@Autowired (或@Resource)
UserMapper userMapper = null;
userMapper.selectById() 操作就好了
整合步骤
Maven配置:
- Spring的版本:4.3.20
- MyBatis的版本:3.4.6
- Spring-Mybaits整合包 1.3.0
- Junit 4.12
- MySQL connectior 驱动 5.1.48
- Druid 1.1.20
使用基于mybatis名为ehome的maven工程
脚手架如下:
注意配置中的maven默认配置文件和仓库位置:
注意maven配置pom中的基本参数
<!--jdk和javaEE的版本-->
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
资源扫描路径的设置每每都是问题爆发的高发点:
<!--配置pom文件中对于资源的扫描路径-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
在dependencies中声明依赖
<!--mybatis核心包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!--spring-context 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!--spring-core 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!-- spring-beans 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!--Spring和Junit的整合单元测试包,可以在Junit中直接使用Spring的自动装配-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<dependency> <!--Mybatis和Spring的整合包, 通过Spring管理SqlSessionFactory, 会创建一个SqlSessionFactoryBean -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- 如要通过Spring直接管理jdbc才需要此包: JdbcTemplate,此次可以省略 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!--单元测试包,若要使用Spring和Junit的整合测试,一定要用4.12以上版本-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 阿里巴巴的数据库连接池druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
可以把Spring的版本统一处理:
<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>4.3.20.RELEASE</spring-version>
</properties>
那么,Spring的相关依赖就可以写为下面这种方式:
<!--spring-context 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<!--spring-core 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-beans 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<!--Spring和Junit的整合单元测试包,可以在Junit中直接使用Spring的自动装配-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-version}</version>
</dependency>
将来,若想要升级或调整Spring的版本,只需要改动Properties中的配置即可
添加完毕后 import
准备db.properties
在resources/properties目录下创建,并添加:
druid.driver=com.mysql.jdbc.Driver
druid.url=jdbc:mysql://localhost:3306/sasu?useSSL=false&characterEncoding=utf8
druid.username=root
druid.password=root
此处druid前缀作为一个标记,表明连接池的配置特点,不是必须的
准备Spring和MyBatis的配置文件
准备Spring的配置文件
通过context:property-placeholder加载db.properties配置文件
<context:property-placeholder location="classpath:properties/db.properties" />
扫描指定包路径,自动装载目标Bean(带有特殊功能的注解@Component @Service @Controller)
<context:component-scan base-package="com.gxa.ehome" />
配置数据源dataSource,通过druid第三方数据源/连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${druid.url}" />
<property name="driverClassName" value="${druid.driver}" />
<property name="username" value="${druid.username}" />
<property name="password" value="${druid.password}" />
</bean>
配置SqlSessionFactory , 通过整合包中提供的Bean
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<!--通过这里加载MyBatis.xml ,通过Spring接管了MyBatis配置文件-->
<property name="configLocation" value="classpath:mybatis.xml" />
<!--通过ref引用的方式, 配置数据源-->
<property name="dataSource" ref="dataSource" />
<!--配置别名,指定父包的目录,该目录下所有的pojo都会自动生成别名(只保留类名)-->
<property name="typeAliasesPackage" value="com.gxa.ehome.pojo" />
</bean>
配置扫描器,用于扫描mapper目录
将扫描到的所有的mapper接口通过动态代理的方式创建实例,并装载到spring的bean容器中进行管理
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" >
<!--mapper所在的基本包的路径-->
<property name="basePackage" value="com.gxa.ehome.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
红色标注的部分在配置时常常搞错: 我们需要配的是一个bean的名字,而不是bean本身,所以要用value而不能用ref;能不能用ref呢?不能,原因是spring和mybaits中存在命名的冲突,导致ref到的目标不是Spring中的bean
MyBatis的配置
<?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>
<!--加载配置文件-->
<!--<properties resource="properties/db.properties" />-->
<settings>
<!--当返回行的所有列都是空时,MyBatis默认返回null-->
<setting name="returnInstanceForEmptyRow" value="true"/>
<!--显示sql语句-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<!--<typeAliases>
<typeAlias alias="SysUser" type="com.gxa.ehome.pojo.SysUser"/>
<package name="com.gxa.ehome.pojo" />
</typeAliases>-->
<!-- <environments default="development">
<environment id="development">
使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域
<transactionManager type="JDBC"/>
mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI
POOLED 表示支持JDBC数据源连接池
UNPOOLED 表示不支持数据源连接池
JNDI 表示支持外部数据源连接池
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>-->
<!--<mappers>
映射文件方式1,一个一个的配置
<mapper resource="SysUserMapper.xml" />
映射文件方式2,自动扫描包内的Mapper接口与配置文件
<mapper class="com.gxa.ehome.mapper.SysUserMapper" />
<package name="com.gxa.ehome.mapper"/>
</mappers>-->
</configuration>
大家注意,我把大部分内容都注释了,保留下来是提醒我们:除了settings其他内容几乎都被Spring接管了
Spring和mybatis的单元测试
先做一个单元测试,测试Mapper是否被Spring注入成功:
@ContextConfiguration(locations = "classpath:spring.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TestSpringMybatis {
@Autowired
SysUserMapper sysUserMapper = null;
@Test
public void test1(){
SysUser byId = sysUserMapper.findById(1);
System.out.println(byId);
}
}
准备代码部分:
pojo:
Mapper: 逆向工程生成的模板
Service
要点:
- 在实现类上增加@Service
- @Resource和@Autowired的区别
- 在业务中操作中直接用mapper调用即可
业务层测试用例
可能会遇到的问题及解决方案:
- DaoSupport : 若使用了JdbcTemplate,需要spring-jdbc 支持 , 通过maven引入
- 逆向工程中, namespace名称中多了一个空格 需要去掉
准备一个事务管理器管理事务
目的:
正常完成就提交,出现异常就回滚 , 即 把业务操作用事务管理
思路:
从配置的角度: 声明事务管理器(配置dataSource)
利用注解的方式 @Tranactional 配置在指定的业务上(Service)
而传统的做法,是spring的声明式事务(<tx: >)
将注解方式驱动起来(spring-config.xml配置)
步骤
创建事务管理器的bean
配置事务管理器 spring提供的 spring-jdbc
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
在UserServiceImpl上增加事务的注解
@Transactional
@Service("userService")
public class UserServiceImpl implements UserService {
将事务管理器的注解方式驱动起来(为了@Transactional)
<tx:annotation-driven transaction-manager="txManager" />
注意: 针对事务的注解驱动,必须是tx的命名空间
如果提示动态代理的错误,则是由于使用了jdk动态代理,改成cglib的方式:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>