新进入一个项目,写了一个功能,就是提供一个服务(service),该服务能够查询和插入。完成后,想要用junit测试一下;发现到了DAO底层注入的SqlSession字段为空;才意识到这是一个Spring注解的项目;之前经验主要是Spring MVC方式开发使用到Spring的注解和依赖注入。另外,这个字段被注解为@Autowired(required=true),在编译器都没有被报错实例化失败,也说明了以着这种测试的方式根本就没有走Spring的容器来管理对象。

  看来是时候要好好研究一下Spring和Mybatis的一些功能本质。

  想要在非Web工程使用Spring,那么需要通过于是添加了applicationContext.xml,定义了服务类bean,然后通过下面的方式获取容器获取对象:

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");// 读取bean.xml中的内容
SmallServiceImplsvr = ctx.getBean("service", SmallServiceImpl.class);// 创建bean的引用对象

  Spring的思想就是容器管理bean,所以无论是他和谁组合,还是让他管理什么,都是向Spring的容器中放入对象。那么,这种放入就有了很多种方式,一种是通过向配置文件中添加配置的传统方式,比如,下面就是添加了service的引用。

<?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-4.0.xsd">
    <context:annotation-config />
    <context:component-scan
        base-package="com.a.b.component, com.a.b.service, com.a.b.dao" />
    <bean id="service" class="com.a.b.service.impl.SmallServiceImpl"></bean>
</beans>

  定义了bean就可以通过上面的代码来获取SmallServiceImpl了。但是对于这段xml有两点说明:

  1. 其实是不需要对annotation-config进行声明,因为component-scan已经具有了annotation-config的职能;

  2.component-scan可以一次性声明扫描多个包(Spring做的扫描是扫描class文件);我就是曾经有一次运行报错,就是因为有一个包没有添加到监视,导致了Spring容器创建bean失败;

  扫描的本质是基于注解(@Service,@Component,@Repository)来发现bean(免于在xml文件进行配置),扫描之后,就是“装配”,类似于Autowired声明是为了Spring容器在实例化bean的过程中来装配字段,所以扫描和装配是两个阶段,后者是基于前者,也不完全依赖(还可以基于配置文件)。

  你会发现一个现象:@Autowired声明的字段的类型都是接口,但是作为装配的类都是通过@Service/@Repository注解声明的实现类。

  另外对于基于注解的说明,对于本例而言,可以不再配置文件中声明service,可以在定义的类头声明@Service(value="service")也可;如果不指定value值,默认的bean的名称为类名首字母小写(smallServiceImp)

@Service(value="service")
public class SmallServiceImpl {

 

  通过getBean获取到了SmallService之后,在运行发现还是报错,因为执行到DAO的时候,发现那个SqlSession的字段(required=true)实例化失败,无法找到候选类;查找了很久原因;其实扫描的是指定的包,无法找到mybaits的类很正常,这个时候就是第三段那段话,和第三方组件配合的时候,第三方组件都是以bean形式定义到了Spring中;另外这个问题查了一段时间是因为我忘记了MyBatis的核心组件就是SqlSession以及SqlSessionFactory,早点想到就能知道需要将其以bean形式注入。

    <bean id="dataSource" name="dataSource"
        class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 指定连接数据库的驱动 -->
        <property name="driverClass" value="dm.jdbc.driver.DmDriver" />
        <!-- 指定连接数据库的URL -->
        <property name="jdbcUrl" value="jdbc:dm://localhost:5236" />
        <!-- 指定连接数据库的用户名 -->
        <property name="user" value="user" />
        <!-- 指定连接数据库的密码 -->
        <property name="password" value="password" />
        <!-- 指定连接池中保留的最大连接数. Default:15 -->
        <property name="maxPoolSize" value="10" />
        <!-- 指定连接池中保留的最小连接数 -->
        <property name="minPoolSize" value="3" />
        <!-- 指定连接池的初始化连接数 取值应在minPoolSize 与 maxPoolSize 之间.Default:3 -->
        <property name="initialPoolSize" value="3" />
        <!-- 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。 Default:0 -->
        <property name="maxIdleTime" value="0" />
        <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数. Default:3 -->
        <property name="acquireIncrement" value="2" />
        <!-- JDBC的标准,用以控制数据源内加载的PreparedStatements数量。 但由于预缓存的statements属于单个connection而不是整个连接池所以设置这个参数需要考虑到多方面的因数.如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 -->
        <property name="maxStatements" value="20" />
        <!-- 每60秒检查所有连接池中的空闲连接.Default:0 -->
        <property name="idleConnectionTestPeriod" value="60" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:sqlmapConfig.xml"></property>
        <property name="mapperLocations" value="classpath:mapper/*Mapper.xml"></property>
        <property name="typeAliasesPackage" value="com.a.b.vo"></property>
    </bean>

    <!-- 配置SQLSession模板 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

  增加了三个bean,定义数据源(dataSource),定义SqlSessionFactory以及SqlSession就不多说什么了。核心就是SqlSessionFactory,dataSource字段说明了数据源信息(此次项目使用的ComboPooledDataSource进行JDBC以及连接池管理);mapperLocation,就是告知mapper文件路径,注意这里路径指的是class路径,因为Spring容器的操作都是在部署之后的环境,所以一定是claas路径;configLocation,对于mybatis的配置(比如mapper参数对象,返回对象的别名,mybatis对于缓存的一些配置都是放置在这个配置文件中;至于typeAliasesPackage,不知。

<?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" />
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="aggressiveLazyLoading" value="false" />
        <setting name="multipleResultSetsEnabled" value="true" />
        <setting name="useColumnLabel" value="true" />
        <setting name="defaultExecutorType" value="REUSE" />
        <setting name="defaultStatementTimeout" value="25000" />
    </settings>

    <typeAliases>
        <package name="com.zcm.mall.vo"/>
    </typeAliases>

</configuration>

  上面这个就是mybatis的配置文件。

  配置好了SqlSession/Factory信息之后,SqlSession字段可以被实例化,而且可以顺利的访问数据库了。

小记

  1. 在mapper文件中,经常会有比较大小的情况">","<",对于这些特殊字符要么采用转义的方式,要么采用<![CDATA[ 这里写你的sql ]]>  这种方式;

  2.在mapper的sql中,可以通过在sql语句中添加<include refid="whereClause" />来添加共通的sql,比如where的过来条件(根据查询bean字段进行拼sql),可能多个select语句会共用,这个时候可以考虑使用。比如分页是一个共同的sql,可以单独放置到一个文件中(比如CommonsqlMapper.xml),然后使用的地方通过include进行引用。

CommonSqlMapper.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="commonSql" >
  <sql id="pageSql">
    <if test="offset != null and limit != null">
      limit #{offset}, #{limit}
    </if>
  </sql>
</mapper>

ASqlMapper.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.zcm.mall.vo.AuditLog">
    <resultMap id="auditLog" type="com.a.b.vo.c">
        <id column="id" property="id" jdbcType="BIGINT" />
        <result column="log_type" property="logType" jdbcType="VARCHAR" />
        <result column="log_msg" property="log" jdbcType="VARCHAR" />
        <result column="oper_Id" property="userId" jdbcType="BIGINT" />
        <result column="oper_name" property="userName" jdbcType="VARCHAR" />
        <result column="create_date" property="createDate" jdbcType="VARCHAR" />
    </resultMap>
    <select id="select" parameterType="auditLog" resultMap="auditLog">
        select * from AUDIT_LOG
        <include refid="whereClause" />
        <include refid="commonSql.pageSql" />
    </select>

  上面的xml中有一个地方定义了resultMap,这个定义用于将数据库中查询结果和实体类进行映射之用。如果你的数据库字段定义的和实体类名称不一致,需要通过这种方式进行映射。