调用mybatisPlus的 Iservice中的save方法,后台日志打印出来Insert语句,但是没插入到数据库表中;...

问题:调用mybatisPlus的 Iservice中的save方法,后台日志打印出来Insert语句,但是没插入到数据库表中;

一共向两个表中插入数据;weather ,向天气表中插入数据成功,但是在异步任务中向 三方同步日志表中 third_request_log 是只打印出了成功Insert 语句和参数,但是实际未插入成功:

代码如下:

package com.atguigu.gulimall.coupon.learn.controller;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.spring.util.ObjectUtils;
import com.atguigu.gulimall.coupon.entity.ThirdRequestLog;
import com.atguigu.gulimall.coupon.entity.WeatherEntity;
import com.atguigu.gulimall.coupon.learn.annotation.ParamValided;
import com.atguigu.gulimall.coupon.learn.entity.CommonConstant;
import com.atguigu.gulimall.coupon.learn.entity.vo.WeatherReqVo;
import com.atguigu.gulimall.coupon.learn.myexception.BusiException;
import com.atguigu.gulimall.coupon.service.ThirdLearnService;
import com.atguigu.gulimall.coupon.service.ThirdRequestLogService;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 调用三方接口 学习
 *
 * @author: jd
 * @create: 2024-05-27
 */
@RestController
@Slf4j
@RequestMapping("/coupon/thirdLearn")
public class HttpThirdCallController {

    @Autowired
    private Environment environment;

    @Autowired
    CommonConstant commonConstant;

    @Autowired
    ThirdLearnService thirdLearnService;

    @Autowired
    ThirdRequestLogService thirdRequestLogService;

    //
    private ExecutorService service = Executors.newCachedThreadPool();

    //0.模拟数据库中的数据
    private static Map<Integer, WeatherReqVo> weatherReqVoMap = null;

    static {
        weatherReqVoMap = new HashMap<Integer, WeatherReqVo>();//创建一个天气表,存储着现有的,可以查询天气的地市
        //初始化表数据
        weatherReqVoMap.put(10001, new WeatherReqVo(10001L, "石家庄"));
        weatherReqVoMap.put(10002, new WeatherReqVo(10002L, "北京"));
        weatherReqVoMap.put(10003, new WeatherReqVo(10003L, "武汉"));
        weatherReqVoMap.put(10004, new WeatherReqVo(10004L, "邯郸"));
        weatherReqVoMap.put(10005, new WeatherReqVo(10005L, "天津"));
        weatherReqVoMap.put(10006, new WeatherReqVo(10006L, "郑州"));
    }
    // 数据来源: 原文链接:https://blog.csdn.net/QQQZSJ/article/details/109116120

    @ApiOperation("第三方调用练习")
    @GetMapping("/thirdCall")
    @ParamValided
    public String getWeather(WeatherReqVo weatherReqVo) {
        //校验所传地址在数据库中是否存在
        if (!weatherReqVoMap.containsValue(new WeatherReqVo(weatherReqVo.getId(), weatherReqVo.getCityName()))) {
            //这里抛出的是自定义的异常,而且这个自定义的异常是被自定义的异常捕捉器给捕捉
            throw new BusiException("所查询地市不在数据库可查询地市列表中", CommonConstant.ErrorCode.NO_VALUE_ERROR);
        }
        //调用三方接口,获取天气
        getWeatherMethod(weatherReqVo);
        return "查询成功";

    }

    /**
     * 调用三方天气查询接口
     *
     * @param weatherReqVo
     */
//    @Transactional(rollbackFor = Exception.class)
    public void getWeatherMethod(WeatherReqVo weatherReqVo) {
        //组合参数
        HashMap<String, Object> params = new HashMap<>();
        params.put("city", weatherReqVo.getCityName());
        //获取API_KEY
        params.put("key", environment.getProperty("API_KEY"));  // 另外一种获取方式  CommonConstant.API_KEY
        //拼接参数(包含参数编码)
        String queryParams = urlencode(params);
        log.info("===========>{}", queryParams);

        //原始Connection get请求方式
//        String response = doGet(CommonConstant.API_URL, queryParams);
        //原始Connection post请求方式
//        String response = doPost(CommonConstant.API_URL, queryParams);

        //hutool 工具 HttpRequest.post() post请求方式
//        String response = httpRequestPost(CommonConstant.API_URL, weatherReqVo.getCityName(), environment.getProperty("API_KEY"));

        //springRestTemplate 请求第三方接口 post方式
        String response = springRestTemplate(CommonConstant.API_URL, params, queryParams);

        log.info("===========>JSONUtil.parseObj(response)\n{}", JSONUtil.parseObj(response));

        JSONObject jsonObject = JSONUtil.parseObj(response);
        int error_code = jsonObject.getInt("error_code");
        if (error_code == 0) {
            System.out.println("调用接口成功");
            JSONObject result1 = JSONUtil.parseObj(jsonObject.getStr("result"));
            JSONObject result = jsonObject.getJSONObject("result");
            log.info("========>JSONUtil.parseObj(jsonObject.getStr(\"result\"))\n  {}", result1);
            System.out.println("======间隔======");
            log.info("========>jsonObject.getJSONObject(\"result\")\n {}", result);
            JSONObject realtime = result.getJSONObject("realtime");
            System.out.println("=============今日天气===============");
            //查询地市
            System.out.printf("城市:%s%n", result.getStr("city"));
            //今日天气
            System.out.printf("天气:%s%n", realtime.getStr("info"));
            System.out.printf("温度:%s%n", realtime.getStr("temperature"));
            System.out.printf("湿度:%s%n", realtime.getStr("humidity"));
            System.out.printf("风向:%s%n", realtime.getStr("direct"));
            System.out.printf("风力:%s%n", realtime.getStr("power"));
            System.out.printf("空气质量:%s%n", realtime.getStr("aqi"));
            //保存今日天气
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            WeatherEntity weatherEntity = new WeatherEntity();
            weatherEntity.setCity(result.getStr("city"));
            weatherEntity.setDate(simpleDateFormat.format(new Date()));
            weatherEntity.setTemperature(realtime.getStr("temperature"));
            weatherEntity.setHumidity(realtime.getStr("humidity"));
            weatherEntity.setInfo(realtime.getStr("info"));
            weatherEntity.setDirect(realtime.getStr("direct"));
            weatherEntity.setPower(realtime.getStr("power"));
            weatherEntity.setAqi(realtime.getStr("aqi"));
            LocalDateTime now = LocalDateTime.now();
            weatherEntity.setCreateTime(now);
            thirdLearnService.save(weatherEntity);
            log.info("===============>今日天气保存成功");

            //批量保存使用
            ArrayList<WeatherEntity> weatherEntityArrayList= new ArrayList<>();
            //未来五天天气
            System.out.println("=============未来五天天气===============");
            JSONArray future = result.getJSONArray("future");
            for (int i = 0; i < future.size(); i++) {
                //未来日期
                String date = future.getJSONObject(i).getStr("date");
                //未来某天温度
                String temperature = future.getJSONObject(i).getStr("temperature");
                //未来某天天气
                String weather = future.getJSONObject(i).getStr("weather");
                //未来某天风向
                String direct = future.getJSONObject(i).getStr("direct");
                System.out.printf("%s %s 天气%n", date, result.getStr("city"));
                System.out.printf("温度:%s%n", temperature);
                System.out.printf("天气:%s%n", weather);
                System.out.printf("风向:%s%n", direct);
                System.out.println();

                //保存未来天气
                WeatherEntity futureWeather = new WeatherEntity();
                futureWeather.setCity(result.getStr("city"));
                futureWeather.setDate(date);
                futureWeather.setTemperature(temperature);
                futureWeather.setInfo(weather);
                futureWeather.setDirect(direct);
                LocalDateTime nowTime = LocalDateTime.now();
                futureWeather.setCreateTime(nowTime);
                weatherEntityArrayList.add(futureWeather);
            }
            //批量保存
            thirdLearnService.saveBatch(weatherEntityArrayList);
            log.info("===============>未来天气保存成功");

            //异步存入日志表-查询成功
//            service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_SUCCESS));
            //同步插入日志 -查询成功
            insertRequestLog(weatherReqVo.getCityName(), CommonConstant.REQUEST_FAIL);
        } else {
            System.out.println("调用接口失败,失败原因" + jsonObject.getStr("reason"));
            throw new BusiException("查询失败!", CommonConstant.ErrorCode.BUSI_ERROR);  //因为这种异常 我做了全局的异常捕获,所以报错的时候,会报出具体的原因,这种的肯定更好
//            throw new RuntimeException("查询失败!");  // 因为对这种异常没有做全局的捕获,所以请求完报错后直接提示错误信息 500,没有提示具体的原因 "status": 500, "error": "Internal Server Error",

            //异步存入轨迹表-查询成功
//            service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_FAIL));

        }

    }
/**
     * RestTemplate 进行第三方接口调用
     * get请求:三种方式 :
     *             1、restTemplate.getForObject(allUrl, String.class, params)  url中的参数用占位符,并且将参数通过map的方式放到getForObject()方法参数中 这种调用成功
     *             2、restTemplate.getForObject(allUrl, String.class) 直接将参数拼接到url里面,不在   getForObject 中传参。这种调用失败
     *             3、restTemplate.exchange(allUrl,
     *                 HttpMethod.GET,
     *                 request,
     *                 String.class,
     *                 params)  这种请求是带请求头的,url传参和第一种的是一样的,也是url是有占位符,最后一个参数是“参数Map”
     *
     * @param apiUrl
     * @param queryParams
     * @return
     */
    public String springRestTemplate(String apiUrl, Map params, String queryParams) {


        Map<String, Object> map;
        RestTemplate restTemplate = new RestTemplate();
        //第一种调用通方式-start,调用restTemplate.getForObject   https://www.cnblogs.com/windyWu/p/16872871.html 见博客  url中的参数用占位符,并且将参数通过map的方式放到getForObject()方法参数中,这种是可以调用通过,并返回正确参数
        String allUrl = apiUrl + "?city={city}&key={key}";
        log.info("========> 完整allUrl {}", allUrl);
        String resultObject = restTemplate.getForObject(allUrl, String.class, params);
        //第一种:-end

//        //第二种:-start调用restTemplate.getForObject ,直接将参数拼接到url里面,不在   getForObject 中传参。这种调用失败,响应: {"result":null,"reason":"暂不支持该城市","error_code":207301}
//        String allUrl = new StringBuffer(apiUrl).append("?").append(queryParams).toString();
//        log.info("========> 完整allUrl {}", allUrl);
//        String resultObject = restTemplate.getForObject(allUrl, String.class);
        //第二种:-end

        //第一种和第二种请求方式 都是调用的 restTemplate.getForObject 响应结果是用下面这两行解析
        map = JSONUtil.parseObj(resultObject);  //需要知道的是 为什么这里既能用Map<String, Object> map来接受,也能用JSONObject jsonObject来接受,
        // 因为JSONObject其实就是相当于前面的map结构,里面存储的也是键值对,可以看上面的方法  httpRequestPost() 中的注释部分put方法。也可以很明显的看出JSONObject结构类似于map
        JSONObject jsonObject = JSONUtil.parseObj(resultObject);

/*
        //第三种调用方式-start,带请求头的,
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        // build the request
        HttpEntity request = new HttpEntity(headers);
        // make an HTTP GET request with headers
        ResponseEntity<String> resultObject1 = restTemplate.exchange(
                allUrl,
                HttpMethod.GET,
                request,
                String.class,
                params
        );
       // 第三种方式 调用的 restTemplate.exchange 响应结果是用下面这种方式进行解析,需要拿到响应结果中的getBody,才是用前两种方式得到的响应体
        map = JSONUtil.parseObj(resultObject1.getBody());
        HttpStatus statusCode = resultObject1.getStatusCode();
        int statusCodeValue = resultObject1.getStatusCodeValue();
        HttpHeaders headers1 = resultObject1.getHeaders();
        //第三种-end  */



        //下面代码是三种请求方式公用的;
        int code = Integer.parseInt(map.get("error_code").toString());
        if (code == 0) {
            System.out.println("响应成功");
            log.info("=================>jsonObject \n {}", JSONUtil.toJsonStr(jsonObject));  //将JSONObject 对象转换为 String
            log.info("=================>map \n {}", map);//这行的输出和上面一行的输出是完全一样的(已经验证过了)
            //取值练习
            JSONObject result = jsonObject.getJSONObject("result");
            //获取地市
            String city = result.getStr("city");  //特别注意,如果get之后是一个对象,则用jsonObject.getJSONObject,如果是得到一个基本类型,则是使用JSONObject对象的getStr()、getInt()等方法
            //获取今天温度
            JSONObject realtime = result.getJSONObject("realtime");
            String temperature = realtime.getStr("temperature");
            System.out.printf("===springRestTemplate==%s温度%s%n===", city, temperature);
        }

        return JSONUtil.toJsonStr(resultObject);
    }


    //异步任务
    public class dealTask implements Runnable{
        //(String cityName,String code)
        private String  cityName;
        private String  code;

        public dealTask(String cityName, String code) {
            this.cityName = cityName;
            this.code = code;
        }

        //业务处理
        @Override
        public void run() {
            log.info("===异步存入轨迹表===");
            try {
                //10s后存储日志表
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            insertRequestLog(cityName,code);

        }
    }

    /**
     * 记录请求日志
     * @param cityName
     * @param code
     */
    public void insertRequestLog(String cityName, String code) {
        ThirdRequestLog thirdRequestLog = new ThirdRequestLog();
        thirdRequestLog.setCity(cityName);
        thirdRequestLog.setRequestCode(code);
        LocalDateTime now = LocalDateTime.now();
        thirdRequestLog.setCreateTime(now);
        thirdRequestLogService.save(thirdRequestLog);
    }

    /**
     * URL入参参数 编码
     *
     * @param params
     * @return
     */
    private String urlencode(Map<String, ?> params) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Map.Entry<String, ?> stringEntry : params.entrySet()) {
            try {
                stringBuilder.append(stringEntry.getKey()).append("=").append(URLEncoder.encode(stringEntry.getValue() + "", "UTF-8")).append("&");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        String result = stringBuilder.toString();
        //截取 从第一个字符到最后一个&,之间的字符,用于后面拼接到url后面当入参
        result = result.substring(0, result.lastIndexOf("&"));
        return result;
    }


}
异步插入日志调用:
service.submit(new dealTask(weatherReqVo.getCityName(), CommonConstant.REQUEST_SUCCESS));

日志打印如下:
待截图

猜测原因:
1、

(1)将当前类的@Transactional(readOnly = true)注解去掉,代表当前类的方法修改数据库操作可以生效。 (不涉及)

(2)在当前类的方法外面加上@Transactional(rollbackFor = Exception.class)注解,代表当前类除了加上@Transactional(rollbackFor = Exception.class)注解的方法修改数据库操作可以生效,但是当前类的其他方法修改数据库的操作不能生效。(可能是)
(3)自调用问题:如果你在同一个类的内部直接调用另一个方法,那么Spring的事务管理可能不会生效。你应该将第二个方法移到另一个bean中,并通过注入的方式调用它。(可能是

(4)事务传播行为:如果你调用的第二个方法也带有@Transactional注解,并且你希望它在第一个方法的事务中运行,你需要确保事务的传播行为(propagation behavior)是正确的。默认的传播行为是REQUIRED,它表示如果当前没有事务,则创建一个新事务;如果当前存在事务,则加入该事务。但是,如果第二个方法被配置为不同的传播行为(如REQUIRES_NEW),那么它将启动一个新的事务,这可能不是你想要的 (不涉及)

(5)异常处理:如果第二个方法中的代码抛出了异常,但该异常没有被捕获或正确处理,那么它可能会导致事务回滚。但是,如果异常被捕获并且没有重新抛出(或者抛出了一个不被rollbackFor指定的异常),那么事务可能不会回滚。确保你正确地处理了所有可能导致事务失败的异常。 (不涉及)

(6)日志和打印:你提到的“打印了insert语句”可能是通过日志或调试信息看到的。这并不意味着语句已经执行并成功插入了数据。它只是表示该语句已经被准备并发送到数据库。要确定语句是否真的执行了,你需要检查数据库或更详细的日志输出。

(7)数据库连接和事务隔离级别:确保数据库连接是有效的,并且事务的隔离级别允许你看到预期的更改。在某些情况下,事务的隔离级别可能会导致你无法立即看到其他事务所做的更改。

(8)检查第二个方法的实现:确保第二个方法中的逻辑是正确的,并且没有导致插入失败的代码(如条件语句、循环、异常处理等)。

(9)启用Spring事务调试日志:通过配置Spring的日志级别为DEBUG或TRACE,你可以获得关于事务管理的更多详细信息。这有助于你诊断问题所在。

(10)检查数据库约束和触发器:有时,数据库约束(如唯一性约束、外键约束等)或触发器可能会导致插入失败,即使你的代码逻辑看起来是正确的。确保检查数据库的任何相关约束和触发器。

具体原因待实验出来!
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执键行天涯

码你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值