Spring使用篇(十)—— Spring与MyBatis整合
文章目录
1 导入整合开发包
为了整合Spring框架与MyBatis框架,首先需要导入jar包:mybatis-spring-1.3.0.
2 搭建案例演示环境
第一步,在Spring_Demo项目中创建名为“SpringMyBatis”的普通Java模块。
第二步,在该模块中创建名为lib的文件夹用于存放该模块所需的全部jar包, 具体如下图所示:
第三步,创建如下图所示的包结构, 其中:annotation包中存放Java配置类;mapper包中存放数据库操作接口;pojo包中存放POJO类;sql包中存放MyBatis框架所需的数据库XML文件;test包中存放测试类;database-config.properties属性文件用于存储数据库四要素属性值;log4j.properties为日志配置属性文件;spring-config.xml为Spring框架配置文件;SqlMapConfig.xml为MyBatis框架的核心配置文件。
第四步,在MySQL数据库中创建名为“chapter12”的数据库, 数据库字符集为UTF-8。在该数据库中创建名为“t_role”的数据表,具体表结构如下所示:
第五步,在pojo包下创建名为“Role”的POJO类。 具体代码如下所示:
package com.ccff.spring.pojo;
import org.springframework.stereotype.Component;
@Component
public class Role {
private Long id;
private String roleName;
private String roleNote;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleNote() {
return roleNote;
}
public void setRoleNote(String roleNote) {
this.roleNote = roleNote;
}
@Override
public String toString() {
return "Role{" +
"id=" + id +
", roleName='" + roleName + '\'' +
", roleNote='" + roleNote + '\'' +
'}';
}
}
第六步,在mapper包中创建名为“RoleMapper”的接口。 该接口提供了对表t_role的一系列操作,具体代码如下所示:
package com.ccff.spring.mapper;
import com.ccff.spring.pojo.Role;
import org.springframework.stereotype.Repository;
public interface RoleMapper {
public int insertRole(Role role);
public Role getRoleById(Long id);
public int updateRole(Role role);
public int deleteRole(Long id);
}
第七步,在sql包中创建名为“RoleMapper.xml”的SQL映射文件。 在该映射文件中给出对数据库的具体操作,代码如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ccff.spring.mapper.RoleMapper">
<insert id="insertRole" parameterType="Role" useGeneratedKeys="true" keyProperty="id">
insert into t_role(roleName, roleNote) values (#{roleName}, #{roleNote})
<selectKey resultType="int" keyProperty="id" order="AFTER">
select @@identity
</selectKey>
</insert>
<delete id="deleteRole" parameterType="long">
delete from t_role where id=#{id}
</delete>
<select id="getRoleById" parameterType="long" resultType="Role">
select id, roleName, roleNote from t_role where id = #{id}
</select>
<update id="updateRole" parameterType="Role">
update t_role
set roleName = #{roleName},
roleName = #{roleName}
where id = #{id}
</update>
</mapper>
第八步,在annotation包中创建名为“ApplicationConfig”的Spring Java配置类。 在该配置类中,添加扫描的类所在的包以及加载XML配置资源文件。具体代码如下所示:
package com.ccff.spring.annotation;
import com.ccff.spring.pojo.Role;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ComponentScan(basePackageClasses = {Role.class})
@ImportResource({"classpath:spring-config.xml"})
public class ApplicationConfig {
}
3 配置相关配置文件
在单独使用MyBatis框架时,在配置MyBatis的全局配置文件SqlMapConfig.xml时,必须配置environments数据库环境配置,在该配置中需要配置数据库事务管理以及数据库连接池。但在Spring与MyBatis进行整合时,对数据源的配置将由MyBatis的全局配置中转移到Spring中配置。
3.1 配置数据源
第一步,在spring-config.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--加载属性文件-->
<context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.database.driver}" />
<property name="url" value="${jdbc.database.url}" />
<property name="username" value="${jdbc.database.username}" />
<property name="password" value="${jdbc.database.password}" />
<!--连接池的最大数据库连接数 -->
<property name="maxActive" value="255" />
<!--最大等待连接中的数量 -->
<property name="maxIdle" value="5" />
<!--最大等待毫秒数 -->
<property name="maxWait" value="10000" />
</bean>
</beans>
从上面的配置中可以看出,在配置数据源的过程中,对连接数据库四要素时使用了属性文件的配置形式,因此,需要在database-config.properties中配置连接数据库四要素具体如下:
jdbc.database.driver=com.mysql.jdbc.Driver
jdbc.database.url=jdbc:mysql://localhost:3306/chapter12
jdbc.database.username=root
jdbc.database.password=root
3.2 配置SqlSessionFactoryBean
第二步,在Spring配置文件中配置SqlSessionFactoryBean。 由MyBatis框架可知,SqlSessionFactory是产生SqlSession的基础,因此配置SqlSessionFactory十分关键。因此,在MyBatis-Spring项目中提供了SqlSessionFactoryBean去支持SqlSessionFactory的配置。配置SqlSessionFactoryBean,还需要配置数据源并引入MyBatis全局配置文件。具体配置如下所示:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--加载属性文件-->
<context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.database.driver}" />
<property name="url" value="${jdbc.database.url}" />
<property name="username" value="${jdbc.database.username}" />
<property name="password" value="${jdbc.database.password}" />
<!--连接池的最大数据库连接数 -->
<property name="maxActive" value="255" />
<!--最大等待连接中的数量 -->
<property name="maxIdle" value="5" />
<!--最大等待毫秒数 -->
<property name="maxWait" value="10000" />
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:SqlMapConfig.xml" />
</bean>
</beans>
3.3 配置SqlMapConfig.xml
第三步,配置MyBatis框架全局配置文件SqlMapConfig.xml。 具体配置如下:
<?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>
<settings>
<!-- 这个配置使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许 JDBC 支持生成的键。需要适合[修改为:适当]的驱动。如果设置为true,则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如 Derby) -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器。SIMPLE 执行器没有什么特别之处。REUSE 执行器重用预处理语句。BATCH 执行器重用语句和批量更新 -->
<setting name="defaultExecutorType" value="REUSE" />
<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置超时时间,它决定驱动等待一个数据库响应的时间 -->
<setting name="defaultStatementTimeout" value="25000"/>
</settings>
<!-- 别名配置 -->
<typeAliases>
<typeAlias alias="Role" type="com.ccff.spring.pojo.Role" />
</typeAliases>
<!-- 指定映射器路径 -->
<mappers>
<mapper resource="com/ccff/spring/sql/RoleMapper.xml" />
</mappers>
</configuration>
3.4 配置MapperFactoryBean
第四步,在Spring配置文件中配置MapperFactoryBean。 根据MyBatis框架的使用方法可知,在MyBatis中,对数据库的一系列操作都放在对应的Mapper XML文件中,而在Java中则仅以接口的形式给出,通过MyBatis的自动映射机制即可在不需要接口实现类的情况下完成映射。
正式因为如此,MyBatis的运行是通过它内部创建的动态代理对象运行的,所以Spring也没有办法为其生成实现类并对其进行管理。为解决这个问题,MyBatis-Spring项目提供了一个MapperFactoryBean类作为中介,我们可以通过在Spring的配置文件中配置MapperFactoryBean来实现我们想要的Mapper。具体配置如下:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--加载属性文件-->
<context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.database.driver}" />
<property name="url" value="${jdbc.database.url}" />
<property name="username" value="${jdbc.database.username}" />
<property name="password" value="${jdbc.database.password}" />
<!--连接池的最大数据库连接数 -->
<property name="maxActive" value="255" />
<!--最大等待连接中的数量 -->
<property name="maxIdle" value="5" />
<!--最大等待毫秒数 -->
<property name="maxWait" value="10000" />
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:SqlMapConfig.xml" />
</bean>
<!-- 配置MapperFactoryBean-->
<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.ccff.spring.mapper.RoleMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
MapperFactoryBean存在3个属性可以配置,分别是mapperInterface、sqlSessionFactory和sqlSessionTemplate。其中:
mapperInterface是映射器的接口;
如果同时配置sqlSessionTemplate和sqlSessionFactory,那么它就会启用sqlSessionTemplate,而sqlSessionFactory作废。
至此,对Spring与MyBatis的整合其实已经完成,已经可以开发测试了。但是,当项目比较大时,这种对每个Mapper文件都进行一个配置的方式显然是很不可取的,因此我们可以通过配置MapperScannerConfigurer,通过它可以用扫描的形式去生成对应的Mapper文件。
3.5 配置MapperScannerConfigurer
第五步,在Spring配置文件中配置MapperScannerConfigurer。 具体配置如下:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--加载属性文件-->
<context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.database.driver}" />
<property name="url" value="${jdbc.database.url}" />
<property name="username" value="${jdbc.database.username}" />
<property name="password" value="${jdbc.database.password}" />
<!--连接池的最大数据库连接数 -->
<property name="maxActive" value="255" />
<!--最大等待连接中的数量 -->
<property name="maxIdle" value="5" />
<!--最大等待毫秒数 -->
<property name="maxWait" value="10000" />
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:SqlMapConfig.xml" />
</bean>
<!-- 配置MapperFactoryBean-->
<!--<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.ccff.spring.mapper.RoleMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>-->
<!-- 配置MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ccff.spring.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!-- 指定标注才扫描成为Mapper -->
<property name="annotationClass" value="org.springframework.stereotype.Repository" />
</bean>
</beans>
通过上面的配置可知,MapperScannerConfigurer主要配置项如下:
basePackage: 指定让Spring自动扫描什么包,它会逐层深入扫描,如果遇到多个包可以使用半角逗号分隔;
sqlSessionFactoryBeanName: 指定在Spring中定义SqlSessionFactory的Bean的名称。如果sqlSessionTemplateBeanName被定义,那么它将失去作用;
annotationClass: 表示如果类被这个注解标识时候,才进行扫描。对于开发而言,建议使用这个方式进行注册对应的Mapper。在Spring中往往使用注解 @Repository 表示数据访问层(DAO)。本篇文章中使用此方法,而不配置markerInterface;
markerInterface: 指定实现了什么接口就人为它是Mapper,需要提供一个公共接口去标记。
3.6 添加@Repository注解
第六步,为Mapper接口添加@Repository注解。 为mapper包中的RoleMapper添加注解@Repository,表示这个一个DAO层,同时为了告诉Spring在扫描Mapper接口文件时扫描该包。这样就可以扫描出对应的Mapper到Spring IoC容器中。具体代码如下所示:
package com.ccff.spring.mapper;
import com.ccff.spring.pojo.Role;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleMapper {
public int insertRole(Role role);
public Role getRoleById(Long id);
public int updateRole(Role role);
public int deleteRole(Long id);
}
4 测试Spring+MyBatis
在test包中创建名为“RoleTest”的测试类用于测试Spring框架整合MyBatis框架后对Role以及数据表t_role进行操作。具体代码如下所示:
package com.ccff.spring.test;
import com.ccff.spring.annotation.ApplicationConfig;
import com.ccff.spring.mapper.RoleMapper;
import com.ccff.spring.pojo.Role;
import org.junit.Before;
import org.junit.Test;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RoleTest {
Role role = null;
RoleMapper roleMapper = null;
ApplicationContext context = null;
@Before
public void getContext(){
context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
role = context.getBean(Role.class);
roleMapper = context.getBean(RoleMapper.class);
}
@Test
public void test(){
/**
* 测试插入
*/
role.setRoleName("role-name-1");
role.setRoleNote("role-note-1");
roleMapper.insertRole(role);
/**
* 测试查询
*/
role = roleMapper.getRoleById(role.getId());
System.out.println(role);
/**
* 测试修改
*/
role.setRoleName("update-role-name-1");
role.setRoleNote("update-role-note-1");
roleMapper.updateRole(role);
/**
* 测试删除
*/
roleMapper.deleteRole(role.getId());
}
}
运行插入测试代码后得到如下日志信息:
查看t_role表得到如下数据(由于之前有测试数据的存在,因此id不是从1开始的):
运行查询测试代码后得到如下日志信息:
运行修改代码得到如下日志信息:
查看t_role表得到如下结果:
运行删除代码后,查看得到如下日志信息:
5、配置SqlSessionTemplate组件
严格来讲,SqlSessionTemplate并不是一个必须配置的组件,但是它也存在一定的价值。首先,它是线程安全的类,也就是确保每个线程使用的SqlSession是唯一且不互相冲突。其次,它提供了一系列的功能,比如增、删、查、改等常用功能,不过在此之前需要先配置它。即在Spring的配置文件中对SqlSessionTemplate组件配置如下:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--加载属性文件-->
<context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" />
<!-- 数据库连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.database.driver}" />
<property name="url" value="${jdbc.database.url}" />
<property name="username" value="${jdbc.database.username}" />
<property name="password" value="${jdbc.database.password}" />
<!--连接池的最大数据库连接数 -->
<property name="maxActive" value="255" />
<!--最大等待连接中的数量 -->
<property name="maxIdle" value="5" />
<!--最大等待毫秒数 -->
<property name="maxWait" value="10000" />
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:SqlMapConfig.xml" />
</bean>
<!-- 配置SqlSessionTemplate-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
<constructor-arg value="BATCH"/>
</bean>
<!-- 配置MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ccff.spring.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!-- 使用sqlSessionTemplateBeanName将覆盖sqlSessionFactoryBeanName的配置 -->
<!-- <property name="sqlSessionTemplateBeanName" value="sqlSessionFactory"/> -->
<!-- 指定标注才扫描成为Mapper -->
<property name="annotationClass" value="org.springframework.stereotype.Repository" />
</bean>
</beans>
SqlSessionTemplate要通过带有参数的构造方法去创建对象,常用的参数是SqlSessionFactory和MyBatis执行器(Executor)类型,取值范围是SIMPLE、REUSE、BATCH。
配置好SqlSessionTemplate后,在test包中的RoleTest测试类中添加名为“testSqlSessionTemplate”的测试方法,具体代码如下:
package com.ccff.spring.test;
import com.ccff.spring.annotation.ApplicationConfig;
import com.ccff.spring.mapper.RoleMapper;
import com.ccff.spring.pojo.Role;
import org.junit.Before;
import org.junit.Test;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RoleTest {
Role role = null;
RoleMapper roleMapper = null;
ApplicationContext context = null;
@Before
public void getContext(){
context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
role = context.getBean(Role.class);
roleMapper = context.getBean(RoleMapper.class);
}
@Test
public void testSqlSessionTemplate(){
SqlSessionTemplate sqlSessionTemplate = context.getBean(SqlSessionTemplate.class);
//添加用户
Role role = context.getBean(Role.class);
role.setRoleName("role-name-sqlSessionTemplate");
role.setRoleNote("role-note-sqlSessionTemplate");
sqlSessionTemplate.insert("com.ccff.spring.mapper.RoleMapper.insertRole",role);
//查询用户
Long id = role.getId();
role = sqlSessionTemplate.selectOne("com.ccff.spring.mapper.RoleMapper.getRoleById",id);
System.out.println(role);
//更新用户
role.setRoleName("update-role-name-sqlSessionTemplate");
sqlSessionTemplate.update("com.ccff.spring.mapper.RoleMapper.updateRole",role);
//删除用户
sqlSessionTemplate.delete("com.ccff.spring.mapper.RoleMapper.deleteRole",id);
}
@Test
public void test(){
/**
* 测试插入
*/
role.setRoleName("role-name-1");
role.setRoleNote("role-note-1");
roleMapper.insertRole(role);
/**
* 测试查询
*/
role = roleMapper.getRoleById(role.getId());
System.out.println("【查询到的角色信息为:】\n"+role);
/**
* 测试修改
*/
role.setRoleName("update-role-name-1");
role.setRoleNote("update-role-note-1");
roleMapper.updateRole(role);
/**
* 测试删除
*/
roleMapper.deleteRole(role.getId());
}
}
运行该测试方法我们发现,当运行一个SqlSessionTemplate时,它就会重新获取一个新的SqlSession,也就是说每一个SqlSessionTemplate运行的时候会产生新的SqlSession,所以每一个方法都是独立的SqlSession,这意味着它是线程安全的。
SqlSessionTemplate需要使用字符串表明运行哪一个SQL,字符串不包括业务含义,只是功能性代码,并不符合面向对象的规范。与此同时,使用字符串时,IDE无法检测代码逻辑的正确性,所以这样的用法逐渐被开发者抛弃了。
最后值得注意的是SqlSessionTemplate允许配置执行器的类型,当同时配置SqlSessionFactory和SqlSessionTemplate的时候,SqlSessionTemplate的优先级大于SqlSessionFactory。