MyBatis和Spring的优点
工欲善其事,必先利其器。开发环境配置好才能正常地编辑和编译代码,就像盖起一座高楼大厦最重要的是打地基。假如要开发Web应用,那么基本离不开数据的存储和处理,也就是离不开数据库。MyBatis解决了JDBC硬编码的问题,把SQL语句配置在了xml文件里,同时mapper映射配置文件可以将数据库操作结果集映射到相应的Java实体类中,十分方便。和Spring整合后,数据库连接池的配置将会放到Spring中,封装JDBC对事务的处理。MyBatis通过SqlSessionFactory来加载全局配置文件和映射配置文件等,初始化出SqlSession会话,提供数据库连接,Spring可以负责管理SqlSessionFactory,管理mapper映射文件,提供事务处理。所以MyBatis和Spring的整合,相得益彰,这篇日志就来看下两者的整合过程,以及使用mapper代理配置后,如何更方便地进行事务处理。
Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring配置文件 -->
<!-- 添加Spring的xmlns和xsi信息 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 加载数据库静态属性配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
首先是Spring配置文件,开头声明了xml版本信息1.0和编码信息UTF-8后,接下来在beans标签对中配置xmlns文档信息(类似于URL)。xsi是模式文档信息,即xml规范。之后第21行,配置数据库连接信息,和MyBatis一样,我们把数据库的连接的配置信息放在了外部的文件里,在Spring配置文件中,通过读取外部静态属性文件,来和数据可建立连接,进行交互。Spring使用的也是DBCP数据库连接池(或者说数据源),这样做的好处是,数据库连接配置信息无需写入到应用程序的代码中,与SQL语句无需硬编码道应用程序代码中一样。来看看保存着数据库连接配置信息的外部静态属性文件:
#JDBC
jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis_test?characterEncoding=utf-8&useSSL=false
jdbc.username = root
jdbc.password = 12345
用户名密码等信息的参数都配置在了这里面,Spring只要读取它们即可。
回到Spring配置文件上,下一个标签配置数据库连接池:
<!-- 数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
数据源id为“dataSource”,配置class为DBCP数据源管理类,指定销毁方法名“close”。可以看到,数据库驱动,用户名,密码等信息,我们都可以通过“${ }”的方式读取,把前面db.properties文件中的参数注入到变量中,前提是我们前面配置了数据库连接配置的外部文件信息。最后到会话工厂SqlSessionFactory的配置,前面说到,Spring负责管理SqlSessionFactory,提供数据库的连接和事务管理,数据库连接信息我们已经配置好了,事务管理方面接着来配置:
<!-- 会话工厂sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 加载MyBatis的全局配置文件 -->
<property name="configLocation" value="mybatis/SqlMapConfig.xml" />
<!-- 数据源文件 -->
<property name="dataSource" ref="dataSource" />
</bean>
对数据库的操作,例如SQL语句,mapper映射配置文件等,我们都配置在了MyBatis的全局配置文件中了,在Spring配置文件里,我们直接把这些东西加载进来。
DAO层配置
由于事务处理部分交由了Spring管理,所以要编写数据库交互层Dao层,它是一个接口,提供了数据库操作的增删查改等方法。以这里的用户查询为例,首先新建一个Dao层接口interface:
package com.sm.dao;
import java.util.List;
import com.sm.po.User;
// 用户管理Dao接口
public interface UserDao {
public User findUserById(int id) throws Exception;
public List<User> multiSearch() throws Exception;
}
里面定义了数据库操作的抽象方法,接着就要创建接口的实现类:
package com.sm.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import com.sm.po.User;
// 查询用户信息UserDap接口的实现类
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
// 查询单个指定id用户
@Override
public User findUserById(int id) throws Exception {
// 继承SqlSessionDaoSupport类, 通过this.getSqlSession()得到sqlSession
SqlSession sqlSession = this.getSqlSession();
User user = sqlSession.selectOne("findUserById", id);
return user;
}
@Override
public List<User> multiSearch() throws Exception {
// TODO Auto-generated method stub
return null;
}
}
实现类实现UserDao接口,同时继承SqlSessionDaoSupport类,这个类是Spring和MyBatis整合包里的,作用是为Spring提供SqlSessionFactory对象,从而生成会话SqlSession,最后通过SqlSession会话实例执行数据库操作,这部分是不是很熟悉?在MyBatis里就是这样做的,现在整合到了Spring中去了。最后返回查询结果集。
接口和对应的实现类完成,最后要在Spring的配置文件中配置这个接口:
<!-- 原始Dao接口 -->
<bean id="userDao" class="com.sm.dao.UserDaoImpl">
<property name="SqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
告诉Spring把SqlSessionFactory作为参数传入该接口,这样接口实现类才能继承SqlSessionFactory类。
MyBatis配置文件
全局配置文件SqlMapConfig
回到了熟悉的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>
<settings>
<!-- 配置日志输出模式logImpl为LOG4J -->
<setting name="logImpl" value="LOG4J" />
<!-- 配置二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 深度延迟加载 -->
<!-- 打开延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 积极加载/按需加载配置 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 批量定义别名 -->
<typeAliases>
<package name="com.sm.po"/>
</typeAliases>
<mappers>
<mapper resource="sqlmap/UserMapper.xml"/>
</mappers>
</configuration>
MyBatis的全局配置文件简单了好多,因为数据源配置,DBCP等信息都整合到了Spring中去了,由Spring来管理,在MyBatis的全局配置文件中,我们只简单地配置日志信息,一些延迟加载,二级缓存信息即可,typeAliases配置别名,直接让Java实体类包作为别名扫描路径,批量配置别名,这样配置后,报下的所有Java实体类都可以拥有一个(除开头字母小写外,其余相同)别名。最后是配置mapper文件的路径,可以单一配置,也可以使用package标签批量配置。
Mapper映射配置文件
下一步,编写mapper映射配置文件,作为简单的MyBatis和Spring整合,我们写个简单的查询语句和映射结果集配置即可:
<?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.sm.po.UserMapper">
<resultMap type="com.sm.po.User" id="userResult">
<id property="id" column="user_id"/>
<result property="username" column="user_name"/>
<result property="gender" column="user_gender"/>
<result property="email" column="user_email"/>
<result property="province" column="user_province"/>
<result property="city" column="user_city"/>
</resultMap>
<select id="findUserById" parameterType="int" resultMap="userResult">
SELECT
U.username as user_name,
U.gender as user_gender,
U.email as user_email,
U.province as user_province,
U.city as user_city
FROM USER U WHERE id=#{id};
</select>
<select id="multiSearch" resultType="user">
SELECT * FROM USER;
</select>
</mapper>
resultMap配置property和column的映射关系,也就是数据库字段和结果集实体类的属性映射关系,主键是id。下面一条简单的查询语句,根据输入id参数来查询相应的用户信息。
测试用例
最后到测试用例,测试用例也比较简单,有了以前MyBatis的基础后,可以轻松写出简单查询用例:
package com.sm.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sm.dao.UserDao;
import com.sm.po.User;
public class UserServiceTest {
// 先获取Spring配置文件对象
private ApplicationContext applicationContext;
// 使用注解@Before,在执行本类中所有测试方法之前,会先执行这个方法
public void setup() throws Exception {
applicationContext = new
ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
}
@Test
public void FindUserByIdTest() throws Exception {
setup();
// 通过配置资源对象获取userDao对象
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// 调用查询方法
User user = userDao.findUserById(4);
StringBuffer result = new StringBuffer();
result.append("用户名: " + user.getUsername() + "\r\n");
result.append("性别: " + user.getGender() + "\r\n");
result.append("电子邮箱: " + user.getEmail() + "\r\n");
result.append("省份: " + user.getProvince() + "\r\n");
result.append("城市: " + user.getCity() + "\r\n");
System.out.println(result.toString());
}
public static void main(String[] args) throws Exception {
UserServiceTest userTest = new UserServiceTest();
userTest.FindUserByIdTest();
}
}
因为我们的事务处理交由了Spring管理,所以先要获取Spring的配置文件,就像之前MyBatis先要通过自己的配置文件初始化SqlSessionFactory然后得到会话SqlSession那样。获取Spring配置文件后,调用getBean()方法获得数据库交互层Dao层的实例后,就可以通过其调用自己写的查询方法了。
Mapper代理配置
从上面Dao层的实现类UserDaoImpl中你可以看到,执行数据库操作是通过SqlSession类的实例来实现,那么它可不可以像MyBatis那样,也是用动态代理呢?可以。Spring有自己的Mapper批量扫面器,首先我们来配置一下:
<!-- Mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定扫描的包名,如果扫描多个包,包之间使用半角逗号分隔 -->
<property name="basePackage" value="com.sm.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
配置批量扫描com.sm.mapper包下的接口,之后就可以去配置Mapper代理接口了:
接口名要和Mapper映射配置文件的namespace一致。最后到测试用例:
package com.sm.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.sm.dao.UserDao;
import com.sm.mapper.UserMapper;
import com.sm.po.User;
public class UserServiceTest {
// 先获取Spring配置文件对象
private ApplicationContext applicationContext;
// 使用注解@Before,在执行本类中所有测试方法之前,会先执行这个方法
public void setup() throws Exception {
applicationContext = new
ClassPathXmlApplicationContext("classpath:spring/applicationContext.xml");
}
@Test
public void FindUserByIdTest() throws Exception {
setup();
// 通过配置资源对象获取userDao对象
//动态代理方法
UserMapper userMapper = (UserMapper) applicationContext.getBean("userDao");
User user = userMapper.findUserById(4);
StringBuffer result = new StringBuffer();
result.append("用户名: " + user.getUsername() + "\r\n");
result.append("性别: " + user.getGender() + "\r\n");
result.append("电子邮箱: " + user.getEmail() + "\r\n");
result.append("省份: " + user.getProvince() + "\r\n");
result.append("城市: " + user.getCity() + "\r\n");
System.out.println(result.toString());
}
public static void main(String[] args) throws Exception {
UserServiceTest userTest = new UserServiceTest();
userTest.FindUserByIdTest();
}
}
使用动态代理后,我们获取的就不是接口的实现类UserDaoImpl的实例了,而是直接获取代理对象UserMapper的实例,然后就可以通过该实例调用自己的查询方法。
完整代码已上传GitHub:
https://github.com/justinzengtm/SSM-Framework/tree/master/MyBatisAndSpring