springmvc camel mybatis集成实例及分析

最近在学习camel,公司之前做过的项目使用到了camel进行了很多工作。就连数据库的操作也是通过camel来完成的。至于用camel来操作数据库有什么优点,目前就我自己的体会来说,利用camel能简化CRUD操作service层的代码。没用camel以前,各个对象的CRUD操作我都会有对应的service去处理。即使这些service很多都只是简单地继承一个CrudServcie然后用泛型限制一下该service处理的实体对象。这样service的接口和实现类看上去很多,但是重复率极高。如果使用了camel,那么我们就可以用camel来写一个通用的service,这样不管你是什么实体类的操作,只要传入类型和要调用的方法名就可以了。由于涉及到公司机密,所以我不会贴出成熟的源代码,但是我可以提供一个自己的列子。我想只要从这个列子出发,稍作改进就能达到上文所提的效果。

例子的架构是这样的:springmvc camel mybatis
依赖由maven来管理,其pom.xml的内容请下载列子源代码查看。
springmvc的配置就不用贴出来了,随处可见。
这里重点讲一下spring跟配置文件里的一些配置项目,尤其是下面这一段:
<!-- 数据源配置 使用事务控制 -->
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8" />
		<property name="username" value="root" />
		<property name="password" value="fanly" />
		<property name="defaultAutoCommit" value="false" />
	</bean>

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource" />
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="configLocation" value="classpath:SqlMapConfig.xml" />
    </bean>
    <bean id="required" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
        <property name="transactionManager" ref="transactionManager" />
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" />
    </bean>
    <bean id="mybatis" class="org.apache.camel.component.mybatis.MyBatisComponent">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
    <!-- 数据源配置结束 -->
需要注意的是最后一项的配置,通过MyBatisComponent类,camel就知道如何通过用户设置的路由来和mybatis进行交互了。这个MyBatisComponent实现了camel的component接口,至于component接口是用来干什么的,可以参考我的博文或者去查看官方手册。其实你可以把component简单里理解为camelContext和其他系统通信的标准,不同的系统实现了component接口,就可以通过这个接口实现用camel标准API进行通信。
这里需要注意的是,MyBatisComponent有一个configurationUri属性,他的默认值为SqlMapConfig.xml,也就是说在默认情况下MyBatisComponent会去加载类路径下的SqlMapConfig.xml去初始化一些配置和用户编写的mapper文件,你当然可以修改这个默认行为,怎么修改呢?通过property注入你的配置文件位置呗。这里需要指出的是,之前我们单独使用mybatis的时候,一种方式是定义一个mapper接口,然后在对应的mapper.xml中将该mapper问价的namespace设置为mapper接口的全路径,这样在运行时,mybatis会利用mapper.xml生成的代理类来作为mapper接口的实现类为程序提供数据访问层的服务。那么我们使用MyBatisComponent来和数据库交互的时候,还要不要定义mapper接口呢?事实证明,我们不再需要定义mapper接口,我们只需要实现mapper.xml即可,那么MyBatisComponent是如何加载到我们所实现的mapper.xml的呢?我们只要在SqlMapConfig.xml中指定我们的mapper.xml文件即可。本实例代码的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" />
	</settings>

	<mappers>
		<mapper resource="com/ugarden/mapper/UserMapper.xml" />
	</mappers>
	
</configuration>
我们再看看UserMapper.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.ugarden.repository.UserMapper">

	<resultMap id="UserResult" type="com.ugarden.entity.User">
		<result property="id" column="id" />
		<result property="email" column="email" />
		<result property="realName" column="real_name" />
		<result property="password" column="password" />
	</resultMap>

	<sql id="columns">
       <![CDATA[
          id,password,email,show_name,real_name
         ]]>
	</sql>

	<insert id="batchInsertUsers" parameterType="list" useGeneratedKeys="false">
    <![CDATA[
        INSERT INTO kf_user (
         	id ,
        	password ,
        	email ,
        	real_name
        ) VALUES 
      ]]>
	<foreach collection="list" item="item" separator=",">
      <![CDATA[
     	  ( 
     	  	#{item.id} ,
        	#{item.password} ,
        	#{item.email} ,
        	#{item.realName} 
          )
   	    ]]>
	</foreach>
	</insert>

	<insert id="insert" parameterType="com.ugarden.entity.User" useGeneratedKeys="false" keyProperty="id">
    <![CDATA[
        INSERT INTO kf_user (
        	id ,
        	password ,
        	email ,
        	real_name 
        ) VALUES (
        	#{id} ,
        	#{password} ,
        	#{email} ,
        	#{realName} 
        )
    ]]>
	</insert>

	<update id="update" parameterType="com.ugarden.entity.User">
    <![CDATA[
        UPDATE kf_user SET
	        password = #{password} ,
	        email = #{email} ,
	        real_name = #{realName} ,
        WHERE 
        	id = #{id}
    ]]>
	</update>

	<delete id="deleteUsersRolesById" parameterType="string">
	 <![CDATA[
        DELETE FROM kf_user_role
        WHERE 
          user_id = #{userId}
	  ]]>
	</delete>

</mapper>


我们在这里也设置了 namespace="com.ugarden.repository.UserMapper",但是我的项目里是没有对应的接口的,这里不设置会不会出问题在写本文的时候还没有试验。写上总是好些,免得让人感到迷茫。
那么我们如何通过camel的API来操作数据库呢?下面是UserServcie.java的内容:

@Service
public class UserService {
	@Autowired
	private ProducerTemplate producerTemplate;
	@Autowired
	private CamelContext camelContext;
	
	public void insertUser() throws Exception {
		
		//init test user entity
		User user = new User();
		user.setEmail("fanly" + System.currentTimeMillis() + "@126.com");
		user.setPassword("123456");
		user.setId(String.valueOf(System.currentTimeMillis()));
		user.setRealName("张双");
		
		Exchange in = this.camelContext.getEndpoint("direct:start").createExchange(ExchangePattern.InOut);
		in.getIn().setBody(user);
		
		Exchange out = this.producerTemplate.send("mybatis:insert?statementType=Insert", in);
		
		if (null != out.getException()) {
			throw out.getException();
		}
		
	}
}


略懂camel的童鞋们立马就明白了,这里首先通过camelContext获取到一个Endpoint,然后获取到输入过程的Exchange对象,由于数据的来源是我们程序提供的,所以endpoint的uri就设置为direct:start。然后将我们要添加的user对象添加到message对象的body中,再将我们的message对象路由到数据库中,我们是通过producerTemplate对象向camelContext对象发出消息的,路由信息附加在了第一个参数中,即"mybatis:insert?statementType=Insert"如果你不懂这个参数的意思,去camel官方看看camel-mybatis的说明就明白了。这句的意思大概就是告诉camelContext对象,我要通过mybatis这个component对象调用一个名叫insert的方法,该方法的statement类型为Insert类型,将Message对象body里的数据插入到数据库。
你可能会问,你这个service里面的producerTemplate,camelContext是哪里来的,为什么你通过一个"mybatis:insert?statementType=Insert"参数,camelContext就知道要去找那个component来进行路由呢?
莫慌,请看spring根配置文件的如下配置内容:
 <!-- camel context inti -->
 <camelContext id="camel" trace="true" xmlns="http://camel.apache.org/schema/spring">
    	<package>com.ugarden</package>
</camelContext>
camelContext就是这样来的,spring一启动的时候他就存在了,如果你有多个camelContext实例的时候,你就要用id来区分注入了。
那producerTemplate是哪里来的呢?刚开始我也纠结这个问题,最后看了一下camelContext的createProducerTemplate的方法注释,发现它是和camelContext一起初始化的,这样就能解释为什么spring能帮我们注入了。
那camel如何知道"mybatis:insert?statementType=Insert"中mybatis指的是哪个呢?您还记得这段配置吗?
<bean id="mybatis" class="org.apache.camel.component.mybatis.MyBatisComponent">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
 </bean>
你看看他的ID,我想如果有多个数据源的时候,我再做如下配置:
<bean id="mybatis1" class="org.apache.camel.component.mybatis.MyBatisComponent">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
 </bean>
那么我是不是可以通过"mybatis1:insert?statementType=Insert"这样来路由呢?是不是试过才知道,写完我就去试一试,camel官方的uri支持范围里是绝对没有mybatis1这种东西的。

现在回到文章开始的问题来,我们如何通过camel来做一个统一的service层呢?很简单,我们只需要将要调用的方法名,body里要路由到数据库的对象,通过参数的方式传递进来不就可以容纳一切变化了吗?从此以后我们只需要通过数据表生成以下mapper.xml,再谢谢特殊的sql就好了。只要是数据库的操作,我们都可以通过camel实现的一个superService类搞定。
最后附上整个例子的源代码供童鞋们下载交流。项目在我的资源栏目里,当然是免积分的了。


posted @ 2013-03-27 21:10  xinyuyuanm  阅读(618)  评论(0编辑  收藏  举报