业务上,手机App(离线状态下的数据),在在线的时候需要往服务端上传,由于App上的SQLite数据库里的需要 同步数据的表 跟服务端的数据库表结构一致,所以为了同步数据的方便性,我们决定App在进行insert update delete 操作时,将SQL语句(已拼装好参数的sql)

记录到Sqlite内的一张记录表内,结构如下

package com.darkBlue.web.mobile;

import java.util.Date;

/**
* Created by root on 2018/7/5 0005.
*/
public class OperateLog {
private Long id;
/**
* 操作类型
*/
private String type;
/**
* 拼装好参数的SQL语句
*/
private String statement;
/**
* 拼装好参数的SQL语句
*/
private Date operateDate;
/**
* 操作人
*/
private String operateUser;
/**
* 操作的表名
*/
private String tableName; } get set方法省略

这样的话,APP端需要同步数据,只需要查询这张表的数据,将其传到服务端即可,其中两个字段是必须的 即

type  表示,SQL的类型是update 还是delete 还是insert

statement 表示SQL语句

具体如何上传就不细说了,项目使用springMVC

服务端代码:

首先,服务端想使用jdbcTemplate,需要在applicationContext.xml中进行配置

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>

同时,我们还需要进行事物配置,因为我们需要保证,所有的操作要么全部成功,要么全部失败

 <!-- ===============事务控制的配置 ================-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制住数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean> <!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式) -->
<tx:annotation-driven transaction-manager="transactionManager"/> <aop:aspectj-autoproxy proxy-target-class="true"/>
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut expression="execution(* com.demo.service..*(..))" id="txPoint"/>
<!-- 配置事务增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config> <!--配置事务增强,事务如何切入 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 所有方法都是事务方法 -->
<tx:method name="*"/>
<!--以get开始的所有方法 -->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>

看看上传代码:

package com.demo.web.mobile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; /**
* Created by root on 2018/7/5 0005.
*/
@Controller
@RequestMapping("mobile/uploadOfflineData")
public class UploadOfflineDataController { private static final Logger logger = LoggerFactory.getLogger(UploadOfflineDataController.class);
@Resource
private JdbcTemplate jdbcTemplate; @Resource
private PlatformTransactionManager transactionManager; private static Connection con = null; @RequestMapping("/uploadDB")
@ResponseBody public BaseResponse uploadDB(@RequestBody List<OperateLog> operateLogs) {
// 该bean是我自定义的返回Bean
BaseResponse ret = new BaseResponse(); List<String> insertSqls = operateLogs.stream().filter(t ->
"insert".equals(t.getType())
).map(OperateLog::getStatement).collect(Collectors.toList());
List<String> updateSqls = operateLogs.stream().filter(t ->
"update".equals(t.getType())
).map(OperateLog::getStatement).collect(Collectors.toList());
List<String> deleteSqls = operateLogs.stream().filter(t ->
"delete".equals(t.getType())
).map(OperateLog::getStatement).collect(Collectors.toList());
// 定义事务隔离级别,传播行为
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 事务状态类,通过PlatformTransactionManager的getTransaction方法根据事务定义获取;获取事务状态后,Spring根据传播行为来决定如何开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
try {
con = jdbcTemplate.getDataSource().getConnection();
// 这里设置是没有效果的[是不能实现事物操作的]
// con.setAutoCommit(false);
// 执行顺序不能错
execSQL(insertSqls);
execSQL(updateSqls);
execSQL(deleteSqls);
// 提交status中绑定的事务
transactionManager.commit(transactionStatus);
ret.setStatus(true);
// 这里设置是没有效果的[是不能实现事物操作的]
// con.commit();
} catch (Exception e) {
try {
// 这里设置是没有效果的[是不能实现事物操作的]
// con.rollback();
// 提交status中绑定的事务
transactionManager.rollback(transactionStatus);
} catch (Exception e1) {
logger.error("上传数据,回滚报错,", e1);
}
logger.error("上传数据,SQL报错,", e);
if (e instanceof UploadOfflineDataException) {
ret.setData(e);
} else {
ret.setMsg("未知错误");
}
ret.setStatus(false);
} finally {
try {
con.close();
} catch (Exception e) {
logger.error("上传数据,关闭链接报错,", e);
}
} return ret;
} public void execSQL(List<String> sqls) throws UploadOfflineDataException {
for (String sql : sqls) {
try {
jdbcTemplate.update(sql);
} catch (Exception e) {
// 这是我自定义的异常类
UploadOfflineDataException exception = new UploadOfflineDataException();
String tableName = matchSql(sql);
String info = matchInfo(sql);
if (null != tableName) {
if (tableName.equals("t_jzw_fangjian")) {
exception.setTableName("房间");
exception.setInfo(info);
} else {
exception.setTableName("人员");
exception.setInfo(info);
}
} else {
exception.setTableName("未知错误");
}
throw exception;
} }
} /**
* 表名获取
*
* @param sql lowcase
* @return
*/
public static String matchSql(String sql) {
Matcher matcher = null;
//SELECT 列名称 FROM 表名称
//SELECT * FROM 表名称
if (sql.startsWith("select")) {
matcher = Pattern.compile("select\\s.+from\\s(.+)where\\s(.*)").matcher(sql);
if (matcher.find()) {
return matcher.group(1);
}
}
//INSERT INTO 表名称 VALUES (值1, 值2,....)
//INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
if (sql.startsWith("insert")) {
matcher = Pattern.compile("insert\\sinto\\s(.+)\\(.*\\)\\s.*").matcher(sql);
if (matcher.find()) {
return matcher.group(1);
}
}
//UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
if (sql.startsWith("update")) {
matcher = Pattern.compile("update\\s(.+)set\\s.*").matcher(sql);
if (matcher.find()) {
return matcher.group(1);
}
}
//DELETE FROM 表名称 WHERE 列名称 = 值
if (sql.startsWith("delete")) {
matcher = Pattern.compile("delete\\sfrom\\s(.+)where\\s(.*)").matcher(sql);
if (matcher.find()) {
return matcher.group(1);
}
}
return null;
} /**
* @describe: 错误SQL获取错误的参数
* @params:
* @Author: Kanyun
* @Date: 2018/7/20 11:31
*/
public static String matchInfo(String sql) {
String[] infos = sql.split(" ");
String regex = "^[\\u4e00-\\u9fa5]*$";
Pattern p = Pattern.compile(regex);
List info = new ArrayList();
for (String s : infos) {
Matcher m = p.matcher(s);
if (m.find()) {
info.add(s);
}
}
return info.toString(); }
}

我自定义的异常类 [按业务需求定义字段]

public class UploadOfflineDataException extends Exception {
private String idCard;
private String name;
private String addr;
private String tableName;
private String info;
}

我自定义的返回bean[按业务需求定义字段]

public class BaseResponse implements Serializable {

    private boolean status;
private String msg;
private Object data;

基础类就已经写完了,主要关注的点是jdbcTemplate的事物控制

虽然Connection 可以设置setAutoCommit(false),但是并不能实现事物控制,

原因是因为:

因为jdbcTemplate.getDataSource().getConnection()获取的connection与每次jdbcTemplate.update用到的connection都是从连接池中获取的,不能保证是一个connection

同时需要注意的是,对于Mysql来说,存储引擎对事物的支持也是不一样的,InnoDB支持事物,MyISM不支持事物

所以我采用了spring的编程式事物[Spring事物分两种,一种是编程式事物,一种是声明式事物]

我这里采用编程式事物,主要是因为我要控制代码块,而编程式事物的优点就是事物的管理是代码块级的,而声明式的是方法级的(虽然可以通过重构方法达到和编程式事物一样的效果)

更多详见:https://blog.csdn.net/zhj870975587/article/details/75152604

JDBCTemplate使用的更多相关文章

  1. JdbcTemplate+PageImpl实现多表分页查询

    一.基础实体 @MappedSuperclass public abstract class AbsIdEntity implements Serializable { private static ...

  2. Spring JdbcTemplate

    参考链接: https://my.oschina.net/u/437232/blog/279530 http://jinnianshilongnian.iteye.com/blog/1423897 J ...

  3. jdbcTemplate批量插入(添加)

    public void addSubscibe(List<PermedipUserSubscribeVo> list) { final List<PermedipUserSubscr ...

  4. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  5. Spring MVC篇二、使用JdbcTemplate进行数据库操作

    上一篇只是一个简单的Spring MVC框架,接下来添加一些跟数据库的交互. 一.添加jdbc相关配置   在maven中添加相关依赖后,配置数据库访问参数及数据源.数据库参数使用配置文件,代码如下: ...

  6. 使用Spring JdbcTemplate实现数据库操作

    今天我来演示 关于JDBCTemplate实现对数据库的查询和添加 首先是添加 第一步大家都知道 创建一个实体类 然后写一个方法 把实体类当参数传进去 在实现这个接口 JdbcDaoSupport这个 ...

  7. JdbcTemplate进行查询

    1.jdbcTemplate.queryForInt() 和 jdbcTemplate.queryForLong() 例如:下面使用queryForInt()方法传回user表中的记录数: jdbcT ...

  8. jdbcTemplate之jdbc模板技术

    1:为什么要使用jdbcTemplate? 在实际开发中使用jdbc技术太过复杂,为了减少代码冗余,操作简单 步骤一:创建实体类 package beans; public class Book { ...

  9. Spring JdbcTemplate 方法详解

    JdbcTemplate主要提供以下五类方法: execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句: update方法及batchUpdate方法:update方法用于执行新增.修 ...

  10. Spring中的JdbcTemplate使用

    1.引出SpringJDBC的概念 在学习JDBC编程时我们会感觉到JDBC的操作是多么繁琐,那么当我们学习的Hibernate框架时,我们感觉到数据库的操作也变非常简单,提高了开发效率.但是当使用H ...

随机推荐

  1. 【eclipse】聚合工程maven启动Tomcat报错

    严重: Error configuring application listener of class严重: Skipped installing application listeners due ...

  2. UVa 1601 万圣节后的早晨

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  3. Linux command line exercises for NGS data processing

    by Umer Zeeshan Ijaz The purpose of this tutorial is to introduce students to the frequently used to ...

  4. Python matplot画柱状图(一)

    图的存在,让数据变得形象化.无论多么复杂的东西,都是简单的组合. import matplotlib.pyplot as plt import numpy as np dict = {'A': 40, ...

  5. Python中通过csv的writerow输出的内容有多余的空行

    第一种方法 如下生成的csv文件会有多个空行 import csv #python2可以用file替代open with open("test.csv","w" ...

  6. Meta referrer标签的简要介绍

    在某些情况下,出于一些原因,网站想要控制页面发送给 server 的 referer 信息的情况下,可以使用这一 referer metadata 参数. 参数 referer 的 metedata ...

  7. 30分钟了解如何使用Kafka

    Kafka是当下对海量数据提供了最佳支持的MQ中间件,无论是高并发的处理,还是依托zookeeper的水平拓展都有不俗的特性.由于公司最近也在尝试如何将它应用到开发中以对业务更好的支撑,因此特地分享一 ...

  8. Codeforces 894B - Ralph And His Magic Field

    894B - Ralph And His Magic Field 思路: 当k为1时,如果n和m奇偶性不同,那么没有答案. 可以证明,在其他情况下有答案,且答案为2^(n-1)*(m-1),因为前n- ...

  9. Python string常用函数

    2017-07-03 23:26:08 1..replace(self, old, new, count=-1) replace()函数将旧字符串替换为新字符串,最后一个参数count为可选项,表示替 ...

  10. m_Orchestrate learning system---三十一、模板和需求的关系

    m_Orchestrate learning system---三十一.模板和需求的关系 一.总结 一句话总结:模板为了适应广大用户,有很多功能样式,但是,你需要的只是部分,所以删掉不需要的,如果有需 ...