SSM框架开发web项目系列(五) Spring集成MyBatis
前言
在前面的MyBatis部分内容中,我们已经可以独立的基于MyBatis构建一个数据库访问层应用,但是在实际的项目开发中,我们的程序不会这么简单,层次也更加复杂,除了这里说到的持久层,还有业务逻辑层、视图层等等,随着代码量和需求复杂度逐渐增长,对象的创建、管理以及层与层之间的耦合度等等问题随之而来。Spring是目前比较流行的框架,它解决了诸多例如层次之间松耦合、帮助管理对象等问题。如果是有准备学习MyBatis的人,应该或多或少都学习过或者了解过Spring了,本篇要通过Spring集成MyBatis,首先要对Spring核心的概念IOC和AOP有个基本认识。
首先回顾下IOC,控制反转(Inversion of Control,英文缩写为IOC),前面讲到的层与层之间的耦合问题在Spring中,即通过控制反转解决的,那么控制具体是控制什么,反转是怎样反转?我们在学习初期,创建对象用到最多的往往就是new语句,如果需要一个Person类的实例对象,我们就可以通过 Person p = new Person(); 获取我们想要的,进一步引申,在我们的开发中,如果业务逻辑Service层需要一个持久层Dao的实例,那么我们也可以在这个Service中直接new一个Dao实例,这样就相当于是我们自己在控制这个对象的创建和管理等等,但实际上项目中引入Spring后,我们无需自己去创建对象,而是交由Spring创建和管理,控制反转具体来看,应该是控制对象的创建、管理等等,这些职责的反转,原来是我们的职责,现在反转到了Spring容器那边管理。另外说道IOC,往往还伴随着一个DI(Dependency Injection)依赖注入的概念,前面为什么要在一个层的类中创建(获取)另一个类对象,是因为类与类之间的依赖,如果由我们主动在类中创建对象,会使得项目高耦合,灵活度大大降低。而通过依赖注入可以让我们轻松获取由容器创建和管理的对象,而不需要在程序中硬编码创建对象,从而达到解耦程序。依赖注入,基于Java反射,是控制反转的一种具体实现方式。
AOP(Aspect Oriented Programming)面向切面编程,使用到Java中代理模式,是对面向对象编程的一种补充,而不是竞争或冲突,例如我们看待一段web项目访问过程,首先访问模块A,随之系统会记录访问日志,然后访问模块B,系统也会随之记录访问日志,以面向对象的思维来看,从前到后,这是两段完整的过程,每段过程中又包括我们的访问和系统的日志记录两种操作。但是换到这里的切面思维,我们每访问一个模块,系统都会随之有记录访问日志,把这个记录日志的动作以切面抽取并封装起来,可以减少系统的重复代码,降低模块之间的耦合度,提高系统的可操作性和可维护性。常见应用有例如事务管理、日志管理、安全控制等等。
Spring集成MyBatis
既然Spring容器能帮助我们创建和管理对象,那么在这里,不同于前面的独立MyBatis程序,我们可以把那些获取对象的过程按照Spring的方式做些修改。首先来回顾下前面我们做MyBatis单独测试的方法代码,示例如下:
@Test public void testCore() throws IOException { //直接实例SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //MyBatis配置文件路径 String path = "mybatis-config.xml"; //通过路径获取输入流 Reader reader = Resources.getResourceAsReader(path); //通过reader构建sessionFactory SqlSessionFactory sessionFactory = builder.build(reader); //获取SqlSession对象 SqlSession sqlSession = sessionFactory.openSession(); //获取Mapper实例 DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); Dept dept = mapper.selectById(10001); Company company = dept.getCompany(); //所属公司 List<Emp> empList = dept.getEmpList(); //员工集合 System.out.println(dept); }
这里的SqlSessionFactory、SqlSession、Mapper实例等对象都是由我们手动去自己创建,下面正文开始通过Spring集成方式,我们可以免除这些创建对象的代码。
数据库环境MySql,工具Navicat for MySQL,创建一张Person表如下所示,并填入几条测试数据
新建Maven项目,可以是普通Jar项目,简单选quickstart那个,工程结构如下所示:
POM依赖可参考SSM框架开发web项目系列(一) 环境搭建篇
实体类Person
package com.mmm.pojo; public class Person { private Integer id; //主键 private String name; //姓名 private String gender; //性别 private Integer age; //年龄 private String ifIT; //是否从事IT行业 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getIfIT() { return ifIT; } public void setIfIT(String ifIT) { this.ifIT = ifIT; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", ifIT=" + ifIT + "]"; } }
mybatis-config.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> <!-- 这里可以定义类的别名,在mapper.xml文件中应用会方便很多 --> <typeAliases> <typeAlias alias="psn" type="com.mmm.pojo.Person" /> </typeAliases> <!-- 这里的环境配置中事务管理器和数据源分别在Spring配置文件中去配置,所以原本这里的部分就可以不写了 --> <!-- <environments default="envir"> <environment id="envir"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.0.100:3306/ssm?characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="abc123"/> </dataSource> </environment> </environments> --> <!-- 通过在Spring集成MyBatis配置文件中SqlSessionFactoryBean中的mapperLocations属性配置实现sql映射xml文件的扫描,所以下面的mappers在这里也可以不写了--> <!-- <mappers> <mapper resource="com/mmm/mapper/personMapper.xml"/> </mappers> --> </configuration>
application-root.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" 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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <!-- 引入jdbc参数配置 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- Spring会自动扫描base-package指定包下面添加了@Component @Service等注解的类并将其实例化 --> <context:component-scan base-package="com.mmm" /> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- spring和MyBatis整合 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 自动扫描mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/mmm/mapper/*.xml"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> </bean> <!-- DAO接口所在包名,Spring会自动查找其下的类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.mmm.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- <bean id="personMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.mmm.mapper.PersonMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> --> </beans>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm
jdbc.username=root
jdbc.password=123456
PersonMapper.java
package com.mmm.mapper; import java.util.List; import com.mmm.pojo.Person; public interface PersonMapper { //查找所有Person对象 List<Person> selectAll(); }
personMapper.xml
<?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.mmm.mapper.PersonMapper"> <resultMap type="psn" id="personResultMap"> <id column="ID" property="id" /> <result column="NAME" property="name" /> <result column="GENDER" property="gender" /> <result column="AGE" property="age" /> <result column="IF_IT" property="ifIT" /> </resultMap> <select id="selectAll" resultMap="personResultMap"> select * from person </select> </mapper>
最后我们的测试方法就不用像之前那样创建一连串的对象,可以通过如下方式:
package com.mmm.test; import java.util.List; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.mmm.mapper.PersonMapper; import com.mmm.pojo.Person; public class TestSpringMbs { @SuppressWarnings("resource") @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("application-root.xml"); PersonMapper personMapper = (PersonMapper) context.getBean("personMapper"); List<Person> list = personMapper.selectAll(); for(Person p:list) { System.out.println(p); } } }
最后运行一下测试方法,可以看到如下结果,获取到数据库Person表全部记录,即成功运行
小结
这里通过Spring集成MyBatis的一个很基础的示例,我们能对Spring集成ORM框架的大致过程有个基本认识,不仅仅是MyBatis,类似Hibernate、JPA那块在Spring集成过程中核心步骤大致也包涵了本文中的部分。框架会变,但是有些系统的硬性需求不会变,就好比这里的持久层,你要访问数据库,无论你用MyBatis,还是Hibernate,甚至是原生的JDBC,数据库的访问账号密码,只不过一步步封装到了框架中的数据源;我们的更新提交回退等等,在框架中演化成事务管理。理清这些硬性需求并跟随者去学习,往往能更顺畅的记住和理解。而不是说你学了半天MyBatis,觉得数据库操作什么都没问题了,然后突然换个Hibernate,感觉一片迷茫,认清这些核心的概念,往往能帮助我们快速的举一反三学习新知识。