分布式NoSQL数据库的Spring整合实践
背景
Spring在J2EE应用程序开发框架中占据重要的作用,它实现了轻量级的IoC(控制反转)和AOP(面向切面)容器框架,能够对JavaBean的生命周期进行管理,可以单独使用也可以和其他框架进行整合,如Struts、MyBatis、Hibernate等主流框架。
Spring的IoC技术促进了低耦合,能够将一个对象依赖的其它对象通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。面向切面的特性,允许通过分离应用的业务逻辑与系统级服务(例如审计和事务管理)进行开发,业务逻辑主要完成业务的各种功能,系统级服务负责其它的关注点,例如日志或事务支持。Spring包含并管理应用对象的配置和生命周期,做为一种容器可以根据业务特性创建一个单独的实例或者每次需要时都生成一个新的实例。Spring可以将简单的组件配置组合成为复杂的应用,因此能很好的与其他J2EE应用开发框架进行集成。
NoSQL数据库目前在技术界正在成为主流,因此巨杉数据库SequoiaDB支持海量分布式数据存储,提供常见开发语言驱动程序便于开发人员直接操作数据库中的数据。在大部分企业级应用程序开发过程中,大部分开发人员或者客户倾向于使用标准的SQL语句做为数据操作的标准接口。巨杉数据库利用巨杉SequoiaDB SSQL套件支持标准的SQL语句对数据库中的数据进行各种操作,本文主要讲解巨杉SequoiaDB SSQL套件与常见的J2EE开发框架进行集成以及开发过程中遇到问题的解决思路。
产品介绍
巨杉数据库SequoiaDB是一款分布式非关系型文档数据库,可以被用来存取海量非关系型的数据,其底层主要基于分布式,高可用,高性能与动态数据类型设计,它兼顾了关系型数据库中众多的优秀设计:如索引、动态查询和更新等,同时以文档记录为基础更好地处理了动态灵活的数据类型。PostgreSQL支持标准SQL,巨杉SequoiaDB SSQL套件通过扩展 PostgreSQL功能可以使用标准SQL 语句访问 SequoiaDB 数据库,完成对SequoiaDB 数据库的各种操作。常见J2EE应用持久层开发框架,如Mybatis、Hibernate等支持PostgreSQL数据库,而这些框架能很好的与Spring进行集成。因此巨杉SequoiaDB SSQL套件与Spring集成和PostgreSQL与Spring集成流程相同。
环境搭建
3.1 软件配置
操作系统:windows 7
JDK:1.7.0_80 64位,下载地址为:
Myeclipse:12.0.0
SequoiaDB:1.12.5或以上版本
SequoiaDB SSQL:9.3.4
依赖包:Spring 3.2.4、mybatis 3.3.0、mybatis-spring 1.1.1、mybatis-paginator 1.2.5、postgresql-9.3.jdbc4-20131010.203348-4
本项目主要实现从SequoiaDB中查询数据并分页来展示Spring整合SequoiaDB SSQL整合的整个过程。
创建项目工程如图:
说明:
Spring整合SequoiaDB的时候建议选择DBCP连接池管理数据库连接
SequoiaDB以及SequoiaDB SSQL安装可参考SequoiaDB官网上面的安装和连接器章节
3.2 集合空间和集合创建及数据准备
1. 以sdbadmin用户登录,进入到/opt/sequoiadb/bin目录中,进入到SDB Shell控制台创建集合空间和集合
创建集合空间脚本如下:
var db = new Sdb(); db.dropCS('CS');
创建集合脚本如下:
db.CS.createCL('account_tx');
执行结果如下图:
2. 退出SDB Shell脚本,在linux控制台执行下面命令导入数据:
./sdbimprt --hosts=node03:11810,node02:11810 --type=csv --file=cs.csv -c CS -l account_tx --fields='account_id long,tx_id long,customer_name string,tx_time string,tx_value int,account_overage long,tx_type int,other_side_account long,tx_site string,summary string' -j 3
执行结果如下图:
3. 利用PostgreSQL连接器创建数据库和外部表
创建数据库的脚本如下:
bin/createdb -p 5432 cs
创建外部表的脚本如下:
create foreign table account_tx(account_id bigint,tx_id bigint,customer_name text,tx_time text,tx_value integer,account_overage bigint,tx_type integer,other_side_account bigint,tx_site text,summary text ) server sdb_server options ( collectionspace 'CS', collection 'account_tx' ) ;
执行结果如下图:
4. 在PostgreSQL验证测试数据
验证测试数据脚本如下:
select * from account_tx limit 5;
执行结果如下图:
代码展示
4.1 框架搭建代码展示
1.在web.xml文件初始化配置信息,如Spring listener和Dispatcher以及加载Spring相关配置文件。
具体配置信息如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web=http://java.sun.com/xml/ns/javaee xsi:schemaLocation=http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd id="WebApp_ID" version="2.5"> <display-name>csWeb</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext-*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
2. 数据库连接信息在jdbc.properties中配置,具体信息如下:
jdbc.driver=org.postgresql.Driver jdbc.url=jdbc:postgresql://192.168.1.48:5432/cs jdbc.username=sdbadmin jdbc.password=
3. 数据操作以及事务控制配置信息在applicationContext-dao.xml和applicationContext-transaction.xml中。
applicationContext-dao.xml具体信息如下:
<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.2.xsd 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:jdbc.properties" /> <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}" /> <property name="maxActive" value="30" /> <property name="maxIdle" value="5" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations"> <array> <value>classpath:mapper/*.xml</value> </array> </property> <property name="configLocation" value="classpath:mybatic-config.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com/sequoiadb/cs/mapper"></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> </beans>
applicationContext-transaction.xml具体信息如下:
<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.2.xsd 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 "> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype"> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> <aop:aspectj-autoproxy /> <aop:config> <aop:pointcut id="appService" expression="execution(* com.sequoiadb.cs.service..*Service*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="appService" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="select*" read-only="true" /> <tx:method name="find*" read-only="true" /> <tx:method name="get*" read-only="true" /> <tx:method name="*" /> </tx:attributes> </tx:advice> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
4. spring MVC配置信息在springmvc.xml,具体信息如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns=http://www.springframework.org/schema/beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <context:component-scan base-package="com.sequoiadb.cs.service" /> <context:component-scan base-package="com.sequoiadb.cs.controller" /> <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="com.sequoiadb.cs.controller.converter.CustomDateConverter" /> </list> </property> </bean> </beans>
5. 数据操作采用的是MyBatis框架,MyBatis框架与Spring整合信息在mybatic-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> <typeAliases> <typeAlias type="com.sequoiadb.cs.entity.AccountTx" alias="AccountTx" /> </typeAliases> <plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="postgresql" /> <property name="offsetAsPageNum" value="true" /> <property name="rowBoundsWithCount" value="true" /> <property name="pageSizeZero" value="true" /> <property name="reasonable" value="true" /> <property name="params" value="pageNum=start;pageSize=limit;" /> <property name="supportMethodsArguments" value="true" /> <property name="returnPageInfo" value="check" /> </plugin> </plugins> </configuration>
4.2 业务实现代码展示
Spring整合SequoiaDB SSQL采用MVC设计模式,在Model层将外部表account_tx信息封装到实体类,dao层采用MyBatis框架操作,Service层完成具体的业务逻辑,Controller层完成视图的响应以及各种用户动作业务的实现。
1. Model层account_tx对应实体类在AccountTx类中,具体信息如下:
package com.sequoiadb.cs.entity; import java.io.Serializable; public class AccountTx implements Serializable { private String account_id; private String tx_id; private String customer_name; private String tx_time; private int tx_value; private long account_overage; private int tx_type; private String other_side_account; private String tx_site; private String summary; public String getAccount_id() { return account_id; } public void setAccount_id(String account_id) { this.account_id = account_id; } public String getTx_id() { return tx_id; } public void setTx_id(String tx_id) { this.tx_id = tx_id; } public String getCustomer_name() { return customer_name; } public void setCustomer_name(String customer_name) { this.customer_name = customer_name; } public String getTx_time() { return tx_time; } public void setTx_time(String tx_time) { this.tx_time = tx_time; } public int getTx_value() { return tx_value; } public void setTx_value(int tx_value) { this.tx_value = tx_value; } public long getAccount_overage() { return account_overage; } public void setAccount_overage(long account_overage) { this.account_overage = account_overage; } public int getTx_type() { return tx_type; } public void setTx_type(int tx_type) { this.tx_type = tx_type; } public String getOther_side_account() { return other_side_account; } public void setOther_side_account(String other_side_account) { this.other_side_account = other_side_account; } public String getTx_site() { return tx_site; } public void setTx_site(String tx_site) { this.tx_site = tx_site; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } }
2. 数据操作在AccountTxMapper类中,配置信息在AccountTxMapper.xml中
AccountTxMapper类具体信息如下:
package com.sequoiadb.cs.mapper; import java.util.List; import java.util.Map; import com.sequoiadb.cs.entity.AccountTx; public interface AccountTxMapper { public List<AccountTx> queryAll(Map map) throws Exception;// 查询所有 }
AccountTxMapper.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.sequoiadb.cs.mapper.AccountTxMapper"> <resultMap id="BaseResultMap" type="com.sequoiadb.cs.entity.AccountTx"> <id column="tx_id" property="tx_id" jdbcType="BIGINT" /> <result column="account_id" property="account_id" jdbcType="BIGINT" /> <result column="customer_name" property="customer_name" jdbcType="VARCHAR" /> <result column="tx_time" property="tx_time" jdbcType="VARCHAR" /> <result column="tx_value" property="tx_value" jdbcType="INTEGER" /> <result column="account_overage" property="account_overage" jdbcType="BIGINT" /> <result column="tx_type" property="tx_type" jdbcType="INTEGER" /> <result column="other_side_account" property="other_side_account" jdbcType="BIGINT" /> <result column="tx_site" property="tx_site" jdbcType="VARCHAR" /> <result column="summary" property="summary" jdbcType="VARCHAR" /> </resultMap> <select id="queryAll" resultType="com.sequoiadb.cs.entity.AccountTx" parameterType="java.util.Map"> select * from account_tx where 1=1 <if test="tx_id != null" > and tx_id = #{tx_id,jdbcType=BIGINT} </if> <if test="account_id != null" > and account_id = #{account_id,jdbcType=BIGINT} order by tx_time desc </if> </select> </mapper>
3. Service完成具体的业务逻辑,AccountTxService类定义具体需完成的业务接口,AccountTxServiceImpl类继承AccountTxService完成具体的业务操作。
AccountTxService接口具体信息如下:
package com.sequoiadb.cs.service; import java.util.List; import java.util.Map; import com.sequoiadb.cs.entity.AccountTx; public interface AccountTxService { public List<AccountTx> queryAll(Map map) throws Exception;// 查询所有 }
AccountTxServiceImpl具体实现如下:
package com.sequoiadb.cs.service.impl; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.sequoiadb.cs.entity.AccountTx; import com.sequoiadb.cs.mapper.AccountTxMapper; import com.sequoiadb.cs.service.AccountTxService; @Service("accountTxService") public class AccountTxServiceImpl implements AccountTxService { @Autowired private AccountTxMapper accountTxMapper; @Override public List<AccountTx> queryAll(Map map) throws Exception { return accountTxMapper.queryAll(map); } }
4. Controller完成视图的响应以及各种用户动作业务的实现,具体实现在AccountTxController类中,具体信息如下:
package com.sequoiadb.cs.controller; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.sequoiadb.cs.entity.AccountTx; import com.sequoiadb.cs.service.AccountTxService; import com.sequoiadb.cs.utils.JSONUtils; @Controller @RequestMapping("/accountTx") public class AccountTxController { @Autowired private AccountTxService accountTxService; @RequestMapping(value = "/query", method = { RequestMethod.POST}) @ResponseBody public String queryAll(HttpServletRequest request,HttpServletResponse response) throws Exception { String tx_id = request.getParameter("tx_id"); String account_id = request.getParameter("account_id"); String sEcho = null; int pageNum = 0; // 起始索引 int pageSize = 0; // 每页显示的行数 JSONArray jsonarray = JSONArray.fromObject(request.getParameter("aoData")); for (int i = 0; i < jsonarray.size(); i++) { JSONObject obj = (JSONObject) jsonarray.get(i); if (obj.get("name").equals("sEcho")) sEcho = obj.get("value").toString(); if (obj.get("name").equals("iDisplayStart")) pageNum = obj.getInt("value"); if (obj.get("name").equals("iDisplayLength")) pageSize = obj.getInt("value"); } PageHelper.startPage(pageNum, pageSize); Map map = new HashMap(); if(tx_id != null && tx_id.length() > 0){ map.put("tx_id", Long.parseLong(tx_id)); } if(account_id != null && account_id.length() > 0){ map.put("account_id", Long.parseLong(account_id)); } List<AccountTx> accountTxList = accountTxService.queryAll(map); PageInfo<AccountTx> pagehelper = new PageInfo<AccountTx>(accountTxList); int initEcho = Integer.parseInt(sEcho)+1; Map dataMap = new HashMap(); JSONObject jsonObject = new JSONObject(); jsonObject.put("iTotalRecords", pagehelper.getTotal()); jsonObject.put("sEcho",initEcho); jsonObject.put("iTotalDisplayRecords", pagehelper.getTotal()); jsonObject.put("aaData", pagehelper.getList()); JSONUtils.toJSONString(dataMap); return jsonObject.toString(); } private void sendJsonData(HttpServletResponse response, String data)throws IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out; out = response.getWriter(); out.println(data); out.flush(); out.close(); } }
5. 本项目主要演示从SequoiaDB数据库中,通过SequoiaDB SSQL查询数据并且分页,由于篇幅较大具体前端页面代码不在此展示,可参考附件中index.jsp文件中的源码。具体查询功能结果展示如下图:
总结
从上述整合过程可以看出,Spring整合SequoiaDB SSQL套件和Spring整合其他数据库如PostgreSQL数据库流程一致。整合过程中主要是根据持久层框架的选择如MyBatis、Hibernate来确定Spring的配置信息,并且在持久层框架确定后需将SequoiaDB的Java驱动jar包添加到项目中。