iT邦幫忙

2021 iThome 鐵人賽

DAY 13
1

我: 哇~ 同學你有好多資源是怎麼來的啊?

同學: 你猜猜看,猜錯的話我就要檢查你大腦發育有沒有正常喔~~(冷笑

我: 難道是你自己花很多時間抓的 ? ( 覺得同學怪怪的,向後退了一步

同學: No~ 我設排程讓系統自己跑的。你猜錯了,所以過來讓我看看你發育有沒有正常~!! (強拉

我: ㄚㄚㄚ,不要阿~~


在現代社會中,時間就是金錢,能夠讓機器自己去跑我們就堅決不自己做,像是證交所API
有一個功能是上市個股每 5 秒委託成交統計,我們總不能請一位工程師在電腦前每五秒點一次查詢吧!

Spring Boot本身就有提供排程讓大家去設定,以下有兩個規則需要注意

  • the method should typically have a void return type (if not, the returned value will be ignored)
  • the method should not expect any parameters

1.方法皆不會回傳值,有回傳值也會被忽略

2.方法不會有條件參數需求

好的,這次我們要寫一個排程功能是每天下午5點會對API請求一次,取得資料後會存在資料庫中,
讓我們先新建一個資料庫表單

CREATE TABLE `daily_tranction_stock_data` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `CODE` varchar(45) DEFAULT NULL COMMENT '證券代號',
  `NAME` varchar(45) DEFAULT NULL COMMENT '證券名稱',
  `TRADE_VOLUME` int(11) DEFAULT NULL COMMENT '成交股數',
  `TRADE_VALUE` bigint(20) DEFAULT NULL COMMENT '成交金額',
  `OPENING_PRICE` decimal(10,2) DEFAULT NULL COMMENT '開盤價',
  `HIGHEST_PRICE` decimal(10,2) DEFAULT NULL COMMENT '最高價',
  `LOWEST_PRICE` decimal(10,2) DEFAULT NULL COMMENT '最低價',
  `CHANGE_GAP` decimal(10,2) DEFAULT NULL COMMENT '漲跌價差',
  `CLOSING_PRICE` decimal(10,2) DEFAULT NULL COMMENT '收盤價',
  `TRANSACTION_COUNT` int(11) DEFAULT NULL COMMENT '成交筆數',
  `CREATE_TIME` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '建立時間',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1141 DEFAULT CHARSET=utf8mb4 COMMENT='上市個股日成交資訊';

再來我們更新一下 model-DailyTranctionStockData 的屬性型別

package com.stockAPI.model;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;

import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class DailyTranctionStockData {
	
	//上市個股日成交資訊

//	{
//		  "Code": "string", //證券代號
//		  "Name": "string", //證券名稱
//		  "TradeVolume": "string", //成交股數
//		  "TradeValue": "string", //成交金額
//		  "OpeningPrice": "string", //開盤價
//		  "HighestPrice": "string", //最高價
//		  "LowestPrice": "string", //最低價
//		  "ClosingPrice": "string", //收盤價
//		  "Change": "string", //漲跌價差
//		  "Transaction": "string" //成交筆數
//		}
	
	private Integer id;
	private Timestamp create_time;
	
	@JsonProperty(value="Code")
	private String code;
	
	@JsonProperty(value="Name")
	private String name;
	
	@JsonProperty(value="TradeVolume")
	private Integer trade_volume;
	
	@JsonProperty(value="TradeValue")
	private BigInteger trade_value;
	
	@JsonProperty(value="OpeningPrice")
	private BigDecimal opening_price;
	
	@JsonProperty(value="HighestPrice")
	private BigDecimal highest_price;
	
	@JsonProperty(value="LowestPrice")
	private BigDecimal lowest_price;
	
	@JsonProperty(value="ClosingPrice")
	private BigDecimal closing_price;
	
	@JsonProperty(value="Change")
	private BigDecimal change_gap;
	
	@JsonProperty(value="Transaction")
	private Integer transaction_count;
	
	public DailyTranctionStockData() {
		
	}

	public DailyTranctionStockData(String code, String name, Integer trade_volume, BigInteger trade_value,
			BigDecimal opening_price, BigDecimal highest_price, BigDecimal lowest_price, BigDecimal closing_price,
			BigDecimal change_gap, Integer transaction_count) {
		super();
		this.code = code;
		this.name = name;
		this.trade_volume = trade_volume;
		this.trade_value = trade_value;
		this.opening_price = opening_price;
		this.highest_price = highest_price;
		this.lowest_price = lowest_price;
		this.closing_price = closing_price;
		this.change_gap = change_gap;
		this.transaction_count = transaction_count;
	}

}

我們在repository裡面新增一個 DailyTranctionStockDataRepository 然後裡面新增一個批量新增資料的功能

@Repository
public class DailyTranctionStockDataRepository {
	
	@Autowired
	JdbcTemplate jdbcTemplate;

	@Autowired
	NamedParameterJdbcTemplate namedParameterJdbcTemplate;

	public int[] batchAdd(DailyTranctionStockData[] dailyTranctionStockData_array) {
		List<DailyTranctionStockData> dailyTranctionStockData_list = new ArrayList<DailyTranctionStockData>();
		for(DailyTranctionStockData dailyTranctionStockData:dailyTranctionStockData_array) {
			dailyTranctionStockData_list.add(dailyTranctionStockData);
		}
		 String sql = " INSERT INTO stockapi.daily_tranction_stock_data ( "
				   + " CODE, NAME, TRADE_VOLUME, TRADE_VALUE, OPENING_PRICE, HIGHEST_PRICE,
				   + LOWEST_PRICE, CLOSING_PRICE, CHANGE_GAP, TRANSACTION_COUNT )"   
				   + " VALUES ( :code, :name, :trade_volume, :trade_value, :opening_price, 
				   + :highest_price, :change_gap, :transaction_count "  
				   + " :lowest_price, :closing_price, ) " ;
        SqlParameterSource[] batch =                                                                     SqlParameterSourceUtils.createBatch(dailyTranctionStockData_list.toArray());
		int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(sql, batch);
		return updateCounts;
		
	}
}

然後在TWSIOpenService新增一個功能

package com.stockAPI.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.stockAPI.enumsave.TWSIOpenAPIUrl;
import com.stockAPI.model.DailyTranctionStockData;
import com.stockAPI.repository.DailyTranctionStockDataRepository;
import com.stockAPI.util.TWSIOpenAPIUtil;

@Service
public class TWSIOpenService {
	
	@Autowired
	DailyTranctionStockDataRepository dailyTranctionStockDataRepository;
	
	public DailyTranctionStockData[] getDailyTranctionStockData(){
		DailyTranctionStockData[] resultList =
		TWSIOpenAPIUtil.send(
				TWSIOpenAPIUrl.EXCHANGE_REPORT_STOCK_DAY_ALL.getUrl(),
				TWSIOpenAPIUrl.EXCHANGE_REPORT_STOCK_DAY_ALL.getMethod(),
				DailyTranctionStockData[].class);
		return	resultList;
	}
	
//新增transaction註解可以防止出錯時資料還繼續寫到資料庫內
	@Transactional(rollbackFor = Exception.class)
	public void schedule_AddDailyTranctionStockData() {
		DailyTranctionStockData[] dailyTranctionStockData_array = getDailyTranctionStockData();
		dailyTranctionStockDataRepository.batchAdd(dailyTranctionStockData_array);
	}

}

新增一個package -schedule ,然後新建一個DailySchedule類別

package com.stockAPI.schedule;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.stockAPI.service.TWSIOpenService;

@Component
public class DailySchedule {
	
	 private final Logger logger = LoggerFactory.getLogger(this.getClass());
	 
	 @Autowired
	 TWSIOpenService tWSIOpenService;
	 
	 //上市個股日成交資訊-存入 下午五點寫入
	 @Scheduled(cron = "0 0 17 * * ?") 
	 public void saveDailyTranctionStockData() {
		 tWSIOpenService.schedule_AddDailyTranctionStockData();
		 logger.info("上市個股日成交資訊-存入 下午五點寫入");
	 }
}

一個cron表示式有至少6個(也可能7個)有空格分隔的時間元素。

按順序依次為

  • 秒(0~59)
  • 分鐘(0~59)
  • 小時(0~23)  
  • 天(月)(0~31,但是你需要考慮你月的天數)
  • 月(0~11)  
  • 星期(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
  • "*" 字元代表所有可能的值
  • "?" 使用在月&天兩處,表示不指定值

這樣我們的寫入排程就完成囉!!


上一篇
RestTemplate實作(二)(Day12)
下一篇
Angular-介紹(Day14)
系列文
Angular+Spring Boot API 處理股市資料32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言