8.Spring与Mybatis整合 框架的整合就是软件之间的集成,它很抽象,因此在做整合之前先想好思路。规划好思路然后按照思路一步一步的做就可以实现框架的整合。 8.1.SM整合思路 8.1.1.思路分析 SM整合是中后端框架的整合: Spring框架作为一个优秀的容器级框架在系统整合中一直充当着业务控制中心的作用。是后端的核心。管理着系统中重要的资源、重要的核心类的对象、业务层对象以及持久层Dao对象。 MyBatis作为一个优秀的持久层框架,其优势当然是访问数据库的具体工作,所以交出对重要资源和核心类对象的控制权,专心做好对数据库访问的具体工作。 分析后得出下面的思路。 8.1.2.思路整理 1.控制层 框架:由Spring负责重要的资源、核心类对象、DAO对象的管理。 配置:ApplicationContext.xml ——properties属性文件的载入(可选,推荐) ——数据源以及连接池(必选) ——MyBatis会话工厂bean(必须) ——传统Dao实现类的bean(可选) ——MyBatis动态代理Dao的bean(可选) ——MyBatis动态代理Dao的包扫描 (批量加载bean)(可选,推荐) 2.持久层 框架:由MyBatis负责访问数据库的具体工作,即通过映射文件中的SQL进行数据库操作。 配置:MyBatisConfig.xml(配置文件名称随意,有的人叫SqlMapConfig.xml) ——自定义别名配置(可选,不推荐) ——传统DAO开发方式需要的映射文件配置(可选,看有没有传统DAO的情况而定。) 8.2.Jar包 8.2.1.Spring的jar包 如何从Spring官网取得jar包 1 访问: http://projects.spring.io/spring-framework/ 点击[Reference]进入后, 搜索[Distribution Zip Files] 下面的才是spring的jar包下载的页面,对应的url:http://repo.spring.io/release/org/springframework/spring/ 以下是取得的jar包 8.2.2.Mybatis的jar包 8.2.3.数据库驱动jar 8.2.4.SM集成的jar Spring与MyBatis的集成需要借助这个集成jar包才能把MyBatis的会话工厂、DAO的对象注入到spring的容器中去,没有它我们完不成SM的整合。 Spring与Mybatis整合包的取得地址: https://github.com/mybatis/spring/releases 以下是我们使用的sm整合jar包: 8.2.5.SM整合的其他依赖jar 以下是sm整合的其他依赖包 8.3.创建工程(UTF-8)、导入jar、创建配置文件 其中ApplicationContext.xml是空的,下面在具体添加内容,其他配置文件是之前的工程中拷贝过来的。 8.4.SM环境整合 8.4.1.规划要进行配置的内容 根据上面的思路可知我们今天的整合主要面对的是两个配置文件: ApplicationContext.xml MyBatisConfig.xml 因为只是SM整合,还不是一个完整的系统,我们没有太多的代码可写,只是测试一下我们前面写在映射文件中的SQL在整合环境下好不好用即可。 8.4.2.ApplicationContext.xml Spring配置样本可以从官网取得: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc 从官网取得的文件: <?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <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> <context:property-placeholder location="jdbc.properties"/> </beans> 8.4.3.数据源及属性文件的配置 <!-- 1 properties属性文件的载入 --> <context:property-placeholder location="jdbc.properties"/> <!-- 2 数据源以及连接池配置 --> <bean id="dataSource" class="org.apache.commons.dbcp.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> 基本和官网获取到的一样,只是属性文件的key名稍作修改。 8.4.4.Mybatis会话工厂bean的配置 <说明>(需要掌握) 项目 解释 核心类 org.mybatis.spring.SqlSessionFactoryBean configLocation 核心类重要属性名,用value加载MyBatis核心配置文件 dataSource 核心类重要属性名,用ref引用数据源的id <配置> <!-- 3. 配置会话工厂 --> <!-- 配置会话工程 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 加载MyBatis核心配置文件 --> <property name="configLocation" value="MyBatisConfig.xml" /> <!-- 引用数据源bean,注意使用ref,属性名不要写错 --> <property name="dataSource" ref="dataSource" /> </bean> 8.4.5.将MyBatis的核心配置文件中的配置删除 删除属性文件配置、数据库环境配置,如果有传统DAO,则需保留传统DAO需要使用的这个映射文件。 <?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> <!-- 自定义java bean的别名定义 --> <!-- <typeAliases> <typeAlias type="cn.baidu.pojo.User" alias="user"/> </typeAliases> --> <!-- 配置映射文件 --> <mappers> <!-- 方式一: 根据映射文件在类目录下的相对路径加载映射文件 --> <mapper resource="cn/baidu/mapper/UserMapper.xml" /> </mappers> </configuration> 8.5.传统DAO实现类的bean <说明>(需要掌握) 项目 解释 DAO实现类要继承的父类 org.mybatis.spring.support.SqlSessionDaoSupport 继承父类的作用 由父类统一管理SqlSession对象的打开、关闭,DAO实现类方法只关注业务。 sqlSessionFactory 父类需要的重要属性名,用ref引用会话工厂的id <配置> <!-- 4. 配置传统DAO实现类的bean --> <!-- 配置DAO接口的实例化bean(使用DAO接口的实现类) --> <bean id="userDao" class="cn.baidu.dao.UserDaoImpl"> <!-- 引用会话工厂bean,注意使用ref,属性名不要写错 --> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> DAO接口【UserDao.java】 package cn.baidu.dao; import cn.baidu.pojo.User; public interface UserDao { // 根据id查询用户信息 public User findUserById(Integer id) throws Exception; } DAO实现类【UserDaoImpl.java】 package cn.baidu.dao; import org.apache.ibatis.session.SqlSession; import org.mybatis.spring.support.SqlSessionDaoSupport; import cn.baidu.pojo.User; public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { @Override public User findUserById(Integer id) throws Exception { // DAO接口实现类的方法中通过方法getSqlSession来取得sqlSession对象。 SqlSession sqlSession = this.getSqlSession(); // 根据id查询 User userInfo = sqlSession.selectOne("user.selectUserById", id); return userInfo; } } 这里面没有关闭sqlSession的代码,如果加上会报错,告诉你不需要关闭,由Spring会帮你做。 测试【MyTest.java】 package sm; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.baidu.dao.UserDaoImpl; import cn.baidu.mapper2.OrderMapper; import cn.baidu.pojo.Order; import cn.baidu.pojo.User; public class MyTest { @Test public void test1() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserDao userDao = (UserDao)ac.getBean("userDao"); User userInfo = userDao.findUserById(1001); System.out.println(userInfo); } } 8.6.MyBatis动态代理DAO的bean <说明>(需要掌握) 项目 解释 核心类 org.mybatis.spring.mapper.MapperFactoryBean 核心类作用 这个核心类帮助我们动态代理指定的接口,并加载对应的映射文件,所以二者要同名同目录 mapperInterface 核心类重要属性,value的值表示需要动态代理的接口的完全限定名 sqlSessionFactory 核心类中也继承了org.mybatis.spring.support.SqlSessionDaoSupport,这个父类需要会话工厂对象,用ref引用会话工厂的id <配置> <!-- 5. 配置MyBatis动态代理DAO的bean --> <bean id="orderDao" class="org.mybatis.spring.mapper.MapperFactoryBean"> <!-- 设置需要代理的DAO接口 --> <property name="mapperInterface" value="cn.baidu.mapper2.OrderMapper" /> <!— 因为MapperFactoryBean继承了SqlSessionDaoSupport,所以需要给它传递会话工程bean --> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> DAO接口【OrderMapper.java】 package cn.baidu.mapper2; import cn.baidu.pojo.Order; public interface OrderMapper { // 根据id查询订单信息2 public Order findOrder2ById2(String orderId) throws Exception; } 测试【MyTest.java】 package sm; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.baidu.dao.UserDaoImpl; import cn.baidu.mapper2.OrderMapper; import cn.baidu.pojo.Order; import cn.baidu.pojo.User; public class MyTest { @Test public void test2() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); OrderMapper orderMapper = (OrderMapper)ac.getBean("orderDao"); Order orderInfo = orderMapper.findOrder2ById2("57528381-433e-11e7-ab09-448a5b6dba5c"); System.out.println(orderInfo); } } 8.7.MyBatis动态代理DAO的包扫描 这是真正企业中DAO动态代理的配置方式(注意:要将第五个配置先注释掉,才能测试第六个配置) <说明> 项目 解释 核心类 org.mybatis.spring.mapper.MapperScannerConfigurer 核心类作用 这个核心类帮助我们批量动态代理指定包路径下的接口,是上面的批量形式,所以二者要同名同目录。 basePackage 核心类重要属性名,用value加载需要扫描的包路径,需要多个包时,中间用半角逗号隔开 ·注意:因为是包扫描,所以无法指定具体的id,只需class即可。 程序中需要使用哪个DAO接口时,用DAO接口名字首字母小写作为它的id即可。若接口名字开头 字母有两个及以上字母是大写的(如:TOrderMapper),那么就直接用DAO接口名字作为id; 这个核心类不需要配置会话工厂,它会自动去找Spring容器中注册的会话工厂对象。 <!-- 6. 配置MyBatis动态代理DAO的包扫描 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.baidu.mapper2" /> </bean> DAO接口【OrderMapper.java】同第五个配置。 测试【MyTest.java】 @Test public void test3() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); OrderMapper orderMapper = (OrderMapper)ac.getBean("orderMapper"); Order orderInfo = orderMapper.findOrderById2("57528381-433e-11e7-ab09-448a5b6dba5c"); System.out.println(orderInfo); } 9.Mybatis逆向工程 1.能够帮助我们生成繁琐又没有技术含量的POJO的代码。 2.还能帮助我们生成单表的增删改查的SQL映射文件以及动态代理接口。 之所以能帮我们生成单表的增删改查的处理是因为单表的这些操作是跟业务无关的,而且它生成的这些SQL中的条件都是全条件的(即对全部字段都可以作为条件,根据传递的是哪些字段的条件动态的生成SQL。) 9.1.将逆向工程导入workspace 9.2.逆向工程构成 1 一个配置文件:generatorConfig.xml 2 三个jar包: MyBatis核心jar:mybatis-3.2.7.jar 逆向工程的jar:mybatis-generator-core-1.3.2.jar 数据库驱动:mysql-connector-java-5.1.28-bin.jar 3 一个main函数方法,直接执行可以上传代码 以上内容均不需要我们记住里面的内容,就把整个工程看作一个工具,是官方提供给我们的工具。 9.3.生成代码 直接运行MyGenerator.java生成代码 注意:每次运行前都要将原来生成的代码删除后在运行生成新的代码,如果不先删除,新生成的代码并不会自动覆盖原来的代码文件而是在原来的代码文件中追加一些不可见的字符导致文件损坏,因此必须先删除在重新生成。 9.4.结合当前工程的逆向工程详细代码 黄色部分为重要的修改项: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <context id="testTables" targetRuntime="MyBatis3"> <commentGenerator> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="123"> </jdbcConnection> <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" userId="yycg" password="yycg"> </jdbcConnection> --> <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成PO类的位置 --> <javaModelGenerator targetPackage="cn.baidu.pojo2" targetProject=".src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:mapper映射文件生成的位置 --> <sqlMapGenerator targetPackage="cn.baidu.mapper2" targetProject=".src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="cn.baidu.mapper2" targetProject=".src"> <!-- enableSubPackages:是否让schema作为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 指定数据库表 --> <table schema="" tableName="user"></table> <table schema="" tableName="order1"></table> <table schema="" tableName="order2"></table> <!-- <table schema="" tableName="user"></table> --> <!-- 有些表的字段需要指定java类型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration> 生成的代码: 把这些代码连同包一起拷贝到sm工程中去测试: 在ApplicationContext.xml的包扫描动态代理配置中追加新的包路径 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 设置扫描的包路径,如果需要扫描多个包,中间用半角逗号隔开 --> <property name="basePackage" value="cn.baidu.mapper,cn.baidu.mapper2" /> </bean> 在【MyTest.java】中添加测试方法 @Test public void test4() throws Exception { ApplicationContext ac = new ClassPathXmlApplicationContext("ApplicationContext.xml"); // 创建接口的动态代理对象 Order1Mapper order1Mapper = (Order1Mapper)ac.getBean("order1Mapper"); // 调用逆向工程生成的接口方法,根据主键查询订单信息 Order1 order1 = order1Mapper.selectByPrimaryKey("57528381-433e-11e7-ab09-448a5b6dba5c"); System.out.println(order1.getOrderstatus()); // 调用多条件设置的对象设置条件,然后执行查询 Order1Example example = new Order1Example(); Criteria criteria = example.createCriteria(); List<String> statusList = new ArrayList<String>(); statusList.add("01"); statusList.add("02"); criteria.andOrderstatusIn(statusList); criteria.andUseridEqualTo(1001); List<Order1> orderList = order1Mapper.selectByExample(example); System.out.println(orderList.size()); } 9.5.MyBatis逆向工程的用处 1.可以快速帮助我们生成每个表对应的pojo,省去了我们手动编写这些没有任何技术含量的pojo的编码工作。 2.逆向工程还可以帮助我们生成单表的增删改查的操作接口和对应的映射文件,单表的增删改查是与业务无关的,因此逆向工程可以帮助我们生成。我们可以拿过来直接使用。 9.6.MyBatis逆向工程注意事项 1.每次在重新生成前要把原先生成的代码删除,然后在重新生成,不能覆盖。因为覆盖会使覆盖后的文件中生成一些不可见的字符,这些字符你在eclipse中打开是看不到有任何错误的,但一运行就会出错,原因就是这些不可见字符损坏了文件。所以每次生成前要先删除原先生成的所有代码和包目录。 2.逆向工程只能生成单表的增删改查操作的接口,对于多表关联处理还需要人手工编写。